Skip to content

Releases: richardbiely/gaia-ecs

v0.8.10

12 Oct 17:53
45aaa0d
Compare
Choose a tag to compare

This is release fixed various possible compilation issues present in v0.8.9 and also the amalgamation for single-header/gaia.h.

Release Notes:

Fixed:

Full Changelog: v0.8.9...v0.8.10

v0.8.9

12 Oct 08:50
d4de846
Compare
Choose a tag to compare

This is a maintenance release that fixes some important bugs.

Release Notes:

Fixed:

  • Compilation error when calling core::call_dtor (58940f9)
  • Docker warnings (58c2203)
  • Deleting Is relationships not handled correctly (61e5764)
  • Invalidate query cache when changing inheritance aka (Is,X) (05cb495, 1c73225)
  • Don't copy EntityDesc during World::copy(_n) because names have to be unique (cad910e)
  • Undefined behavior possible with SystemBuilder (906acf5)
  • Unnecessary copy when calculating the query lookup hash (6de152e)
  • query_sort_cond makes use of SortComponentCond (41a25c5)

Tweaked:

  • Less memory consumed for entity->archetype mapping (d4de846)
  • Simplified ANY compilation (d4de846)

Changed:

  • Query serialization id counter no longer static (46aa9c7)
  • Make sure RTTI an exceptions are disabled on all supported platforms (5c1a8fd)
  • Replaced quom with amalgamate (831442b)
  • compile_commands.json always generated in the given build directory and copied over to source folder (1c71def)

Added:

  • Deleting queries from query cache (7497742)
  • make_single_header vscode task for Windows (faa8f77)
  • delegates, signal, sink (bfa4f50)
  • Container::retain (bfa4f50)

Full Changelog: v0.8.8...v0.8.9

v0.8.8

v0.8.7

23 Sep 17:00
9b03c83
Compare
Choose a tag to compare

This is an important bug-fix release with some interesting internal changes.

First and foremost, despite all the unit tests and memory sanitization, some nasty bugs made it through which have been addressed now. Most of them were logical errors, some were more involved, though. The list of fixes is quite extensive so make sure to update. The core should be very stable now.

A few smaller features were added to the project to make your live easier.

Among them, implicit pairs:

ecs::World w;
ecs::Entity e = w.add();
// Old way of adding a pair to an entity e
w.add(e, ecs::Pair(start, pos));
// You can use a simpler notation now
w.add(e, {start, pos});

Component layouts are now defined via GAIA_LAYOUT. The previous version has been deprecated and is no longer recommended:

struct Position {
  GAIA_LAYOUT(SoA); // Treat this component as SoA (structure of array)
  float x, y, z;
};

The query engine started transitioning to a virtual machine. What this is means is that before the query is first run, it breaks the process into a sequence of specialized instructions in the process called compilation. This eliminates the need to run some logic every time a new archetypes is encountered and makes queries more efficient. The virtual machine is going to keep improving over time and will make queries even faster. It will also make it easier to extend the query engine significantly.
One big new feature, although still only in preview, is the introduction of systems as entities. An undocumented way to use systems until now was:
class MoveSystem final: public ecs::System {
  ecs::Query m_q;

public:
  void OnCreated() override {
    m_q = world().query().all<Position&, Velocity>();
  }

  void OnUpdate() override {
    // Update position based on current velocity
    m_q.each([&](Position& p, const Velocity& v) {
      p.x += v.x;
      p.y += v.y;
    });
  }
};

...
ecs::World w;
ecs::SystemManager sm(w);
// Register the system in the manager
auto& mySystem = sm.add<MoveSystem>();
// Disable the system
mySystem.enable(false);
// Enable the system again
mySystem.enable(true);

while (!terminate_program) {
   // Run all systems on the manager
   sm.update();
   // Handle various world-specific stuff (GC, defragmentation, finalize deletion etc.)
   w.update();
}

Systems can now be defined as entities:

SystemBuilder mySystem = w.system()
  .all<Position&, Velocity>()
  // Logic to execute every time the system is invoked.
  .on_each([](Position& p, const Velocity& v) {
    p.x += v.x * dt;
    p.y += v.y * dt;
    p.z += v.z * dt;
  });


// Retrieve the entity representing the system.
Entity mySystemEntity = mySystem.entity();
// Disable the entity. This effectively disables the system.
w.disable(mySystemEntity);
// Enable the entity. This effectively makes the system runnable again.
w.enable(mySystemEntity);

// The system can be run manually ...
mySystem.exec();
// ... or it can be run as the world seems fit
w.update();

This will allow systems to be queried and create complex relationships among them. For instance, you will be able to tell "give me all systems that operate on XYZ and turn them off", or "give me all systems which are a dependency of this one". This has many practical applications in debugging, monitoring and more which we will get into once the system is fully implemented.


Another preview feature is the introduction of a new storage type for components. So far, only archetype storage has been used. Although great in many things, archetypes are a bit slower when it comes to adding/removing components because content of all entities belonging to archetype A needs to be moved to archetype B. To mitigate this issue a new sparse storage made its way into the project. A packed array or a sparse set storage can be used to store components which are likely to be added and removed or if stability of reference is required. Components living inside the sparse storage never move and thus they are fast to add or remove.
struct SparseComponent {
  GAIA_LAYOUT(Sparse); // Store the component in a sparse storage
  uint32_t id;
  uint32_t data;
};
// Define a comparison operator
bool operator==(const SparseTestItem& a, const SparseTestItem& b) {
	return a.id == b.id && a.data == b.data;
}
// Define to_sparse_id function which extracts a unique id among all SparseComponents
template <>
struct gaia::cnt::to_sparse_id<SparseTestItem> {
  static sparse_id get(const SparseTestItem& item) noexcept {
    return item.id;
  }
};

Data structures are implemented, they are not connected to Gaia yet, though. I will write more about this feature and its implications once it fully lands. For now, it can be used like this:

cnt::sparse_storage<PositionSparse> arr;
arr.add(SparseComponent{100, 0});
arr.add(SparseComponent{200, 1});
const bool has100 = arr.has(100); // true
arr.del(100);
const bool has100 = arr.has(100); // false
const auto data = arr.operator[200].data; // returns 1

Release notes

Fixes:
Fixed: Invalid discord link
Fixed: Dangling archetypes possible in the entity->archetype map
Fixed: Build issues with certain preprocessor macro combinations
Fixed: Missing restrict keyword
Fixed: ThreadPool would not handle spurious wake ups
Fixed: shift_elements_right_aos(_n) not working correctly for all kind of types
Fixed: Exclusive NO queries regression
Fixed: Matching of wildcard queries
Fixed: Matching of NO queries
Fixed: Proper return value resolution for container functions front and back
Fixed: Dummy example build + stack allocator on MSVC
Fixed: Out-of-bounds array access in duel
Fixed: Unaligned free used after aligned alloc
Fixed: Empty chunks and archetypes deleted again
Fixed: CantCombine not working properly
Fixed: move_elements_aos would use a const pointer for the address to move from
Fixed: Wrong path in build_clang.sh
Fixed: "no" query operation would not match Is relationships properly
Fixed: world::is and world::in did the opposite of what they should
Fixed: Reliance on an undefined behavior in the unit test
Fixed: Incorrect destructor called when swapping entities inside chunks
Fixed: Wrong memory written to when adding components
Fixed: Make sure docker script files are always in linux format
Fixed: Unused variable that could trigger a warning

Changes:
Changed: ecs::DependsOn –> ecs::Requires
Changed: Use mold/gold linkers when available by default because they perform better than the default linker for clang/GNU
Changed: Dont use mold on Apple
Changed: Catch2 updated to v3.6.0
Changed: Tracy updated to version 0.11.1

Tweaks:
Tweaked: Simplified check for (,) pairs in World::has
Tweaked: Query size update
Tweaked: Further reduction of Query size. Serialization buffer made temporary and moved to World.
[Tweaked: Query invalidation made automatic based on the com...

Read more

v0.8.6

08 Apr 07:28
7d8dfdb
Compare
Choose a tag to compare

This is a maintenance release bringing mostly bug fixes and internal improvements.

New features are of internal nature. In order to have more control over allocations, the project switched to using allocators for everything (we already had ChunkAllocator for handling allocation of memory for archetype chunks). This way, every allocation can be monitored and if necessary, allocation strategy for any given place in the project can be easily changed. Also, this gives the user the ability to override the default allocation with a custom one if needed.

The project supports allocation tracing via Tracy. This gives us the ability to see where any allocation comes from and how the situation changes over time:
tracy_1

Release notes

Fixed:

  • added fallback for setting thread name on Win (e.g. if Win10 is not the target platform) 6de6982
  • build with profiler enabled dd6c809
  • GAIA_LOG_W/E would not output to stderr entirely 08a0fbe
  • possible build errors d55f235
  • ::emplace_back would not return a reference 1e1d22a
  • ecs::call_ctor would not work with braces initialization 1e1d22a
  • incorrect ranges used for indexed view/view_mut dcd9931
  • ecs::Core no longer excluded by default on queries 5b05dba
  • remapping not applied properly for chunk batching 0b3ef53
  • numerical overflow in the app example d2db469

Changed:

  • Picobench and Catch2 version bump b53fdb4
  • all Gaia-ECS allocations go through internal DefaultAllocator by default 58a17a0
  • benchmarks will no longer generate output in profiling mode 5ac6adb
  • use 64b hash for entity lookup 1820ff4
  • World::has_none -> has_no 5b05dba

Tweaked:

Added:

  • DefaultAllocator 58a17a0
  • StackAllocator fcdb1ea
  • support for custom allocator on containers 58a17a0
  • support for named allocation (currently exposed for profiling sessions only) 98b92b0
  • support for mutex profiling 278e757
  • World::set_max_lifespan to change how long an empty archetype stays alive until deleted 506efc5

Full Changelog: v0.8.5...v0.8.6

v0.8.5

11 Mar 18:59
f9eafc9
Compare
Choose a tag to compare

This release brings many stability improvements. Memory and address sanitisation tests were extended and as a result new bugs could be discovered and fixed. Internal support for SoA types was brought 99% on-par with ordinary AoS structures.

New features

Iterator view index

Performance of views can be improved slightly by explicitly providing the index of the component in the query.

ecs::Query q = w.query();
q.any<Something>().all<Position&, Velocity>();

q.each([](ecs::IterAll& it) {
  auto s = it.view<Somehting>(0); // Something is fhe first defined component in the query
  auto p = it.view_mut<Position>(1); // Position is the second defined component in the query
  auto v = it.view<Velocity>(2); // Velocity is the third defined component in the query
  ....
}

Batched entity creation

Many entities can be added or duplicated at once, now. This is more performant than creating entities one by one.

// Create 1000 empty entities
w.add(1000);
w.add(1000, [](Entity newEntity) {
  // Do something with the new entity
  // ...
})

// Create an entity with Position and Velocity.
ecs::Entity e = w.add();
w.add(e, position, Position{0, 100, 0});
w.add(e, velocity, Position{0, 0, 1});

// Create 1000 more entites like "e".
// Their component values are not initilazed to any particular value.
w.add_n(e, 1000);
w.add_n(e, 1000, [](Entity newEntity) {
  // Do something with the new entity
  // ...
});

// Create 1000 more entities like "e".
// Their component values are going to be the same as "e".
w.copy_n(e, 1000);
w.copy_n(e, 1000, [](Entity newEntity) {
  // Do something with the new entity
  // ...
});

New Contributors

  • @abeimler made their first contribution in #20 - this is the first-ever community contributor :)

Release notes

Fixed:

  • compiling Errors by @abeimler in #20
  • various possible compilation issues on different compilers 18d55d7 69d7ea1
  • component comparison fallback function would only compare one byte 541ade3
  • reset thread handles when changing the number of workers 91524bb
  • variable hiding in Roguelike example a16300b
  • broken member function detection a813211
  • broken reflection for size+data serialization 6e09cb0
  • incorrectly done unit test check for serialization 6e09cb0
  • wrong way to delete chunk memory with GAIA_ECS_CHUNK_ALLOCATOR disabled 4601dd4
  • uninitialized chunk memory when building with GAIA_ECS_CHUNK_ALLOCATOR disabled b3e3ae0
  • component cache items never deleted 4601dd4
  • wrong paths in build_clang.sh ec4d7b6
  • span not throwing asserts 1d65f73
  • data alignment in containers 1f32ccd
  • use launder to make sure unnecessary optimizations don't happen to pointers inside placement new area 9364cf7
  • defragmentation wouldn't move data around 5eae076
  • defragmetantion could cause a crash 5eae076
  • World::cleanup leaves the world in an unusable state ea0f0e6 e3255d9
  • full SoA support brought up-to-date with AoS 02d51ea cca7f6f

Changed/tweaked:

  • no need for entity record lookup for new entities d344cb5
  • entity creation modified df5d73b
  • ECS iterators passed by reference from now on 59be1d2 5354961 295a171
  • Improved performance of adding/moving entities 7f6ce62
  • World::bulk renamed to World::build 1c94118)
  • World::set(Entity) renamed to World::acc_mut(Entity) 1c94118
  • World::get(Entity) renamed to World::acc(Entity) 1c94118
  • entity setters now return a reference to data (AoS) or the data accessor object (SoA) 1c94118 73913af
  • extended sanitization 4601dd4
  • ComponentCache::clear made hidden from users ac198f4
  • archetypes keep their index in the list for fast lookup during removals 0d3a770
  • simplified archetypes tracking during defragmentation 5eae076

Added:

Full Changelog: v0.8.4...v0.8.5

v0.8.4

20 Feb 17:57
63c1ea3
Compare
Choose a tag to compare

This is a critical fix version addressing a build issue on Windows platform.

Fixed:

Full Changelog: v0.8.3...v0.8.4

v0.8.3

18 Feb 18:39
c634eb8
Compare
Choose a tag to compare

This release brings some important stability fixes and powerful new features.

Compile time pairs

Previously, to define a pair, you needed to use the entity id.

struct Likes{};
struct Carrot{};
ecs::Entity likes = w.add<Likes>.entity;
ecs::Entity carrot = w.add<Carrot>.entity;
ecs::Query q = w.query().all( ecs::Pair(likes, carrot) );

A shorter version exists now:

struct Likes{};
struct Carrot{};
ecs::Query q = w.query().all( ecs::pair<Likes, Carrot>() );

Pairs with data

Pairs do not need to be formed from tag entities only anymore. You can use components to build a pair which means they can store data, too!
To determine the storage type of Pair(relation, target) the following logic is applied:

  1. if "relation" is non-empty, the storage type is rel.
  2. if "relation" is empty and "target" is non-empty, the storage type is "target".
struct Start{};
struct Position{ int x, y; };
...
ecs::Entity e = w.add();
// Add (Start, Position) from component entities.
ecs::Entity start_entity = w.add<Start>.entity;
ecs::Entity pos_entity = w.add<Position>.entity;
w.add(e, ecs::Pair(start_entity, pos_entity));
// Add (Start, Position) pair to entity e using a compile-time component pair.
w.add<ecs::pair<Start, Position>(e);
// Add (Start, Position) pair to entity e using a compile-time component pair
// and set its value. According the rules defined above, the storage type used
// for the pair is Position.
w.add<ecs::pair<Start, Position>(e, {10, 15});

// Create a query matching all (Start, Position) pairs using component entities
ecs::Query q0 = w.query().all( ecs::Pair(start_entity, pos_entity) );
// Create a query matching all (Start, Position) pairs using a compile-time structure
ecs::Query q1 = w.query().all< ecs::pair<Start, Position> >();

Fixed target queries (first iteration)

It is possible to target multiple source entities from a query now.
This is a feature demo, it is very limited in usage at the moment.

struct Foo { int val; };
ecs::Entity src = w.add();
e.add<Foo>({10});

ecs::Entity foo = w.add<Foo>();
ecs::Query q = w.query().all(foo, src).all<Position>();
q.each([](ecs::Entity entity) {
  // runs for each entity with Position, but only if there is an entity "src" with Foo on it
});

Inheritance (first iteration)

Entities can inherit from other entities by using the (Is, target) relationship. This is a powerful feature that helps you identify an entire group of entities using a single entity.

ecs::World w;
ecs::Entity animal = w.add();
ecs::Entity herbivore = w.add();
ecs::Entity rabbit = w.add();
ecs::Entity hare = w.add();
ecs::Entity wolf = w.add();

w.as(wolf, animal); // equivalent of w.add(wolf, ecs::Pair(ecs::Is, animal))
w.as(herbivore, animal); // equivalent of w.add(herbivore, ecs::Pair(ecs::Is, animal))
w.as(rabbit, herbivore); // equivalent of w.add(rabbit, ecs::Pair(ecs::Is, herbivore))
w.as(hare, herbivore); // equivalent of w.add(hare, ecs::Pair(ecs::Is, herbivore))

bool herbibore_is_animal = w.is(herbivore, animal); // true
bool rabbit_is_herbivore = w.is(rabbit, herbivore); // true
bool rabbit_is_animal = w.is(rabbit, animal); // true
bool animal_is_rabbit = w.is(animal, rabbit); // false
bool wolf_is_herbivore = w.is(wolf, herbivore); // false
bool wolf_is_animal = w.is(wolf, animal); // true

// Iterate everything that is animal
ecs::Query q = w.query().all(Pair(ecs::Is, animal));
q.each([](ecs::Entity entity) {
  // runs for herbivore, rabbit, hare and wolf
});

// Iterate everything that is animal but skip wolfs
ecs::Query q2 = w.query().all(Pair(ecs::Is, animal)).no(wolf);
q2.each([](ecs::Entity entity) {
  // runs for herbivore, rabbit, hare
});

Exclusive no queries

Until now, queries composed of "no" terms were not accepted. This restriction has now been lifted.

ecs::Query q = w.query().no<Position>();
q.each([&](ecs::Iter iter) {
  // Runs for each entity without Position
  ...
  ...
});

Job system improvements

Priorities

Nowdays, CPUs have multiple cores. Each of them is capable of running at different frequencies depending on the system's power-saving requirements and workload. Some CPUs contain cores designed to be used specifically in high-performance or efficiency scenarios. Or, some systems even have multiple CPUs.

Therefore, it is important to have the abilitly to utilize these CPU features with the right workload for our needs. Gaia-ECS allows jobs to be assigned a priority tag. You can either create a high-priority jobs (default) or low-priority ones.

The operating system should try to schedule the high-priority jobs to cores with highest level of performance (either performance cores, or cores with highest frequency etc.). Low-priority jobs are to target slowest cores (either efficiency cores, or cores with lowest frequency).

Where possible, the given system's QoS is utilized (Windows, MacOS). In case of operating systems based on Linux/FreeBSD that do not support QoS out-of-the-box, thread priorities are utilized.

Note, thread affinity is not managed and left on default because this plays better with QoS and gives the operating system more control over scheduling.

// Create a job designated for performance cores
mt::Job job0;
job0.priority = JobPriority::High;
job0.func = ...;
tp.sched(job0);

// Create a job designated for efficiency cores
mt::Job job0;
job0.priority = JobPriority::Low;
job0.func = ...;
tp.sched(job0);

Threads

The total number of threads created for the pool is set via ThreadPool::set_max_workers. By default, the number of threads created is equal to the number of available CPU threads minus 1 (the main thread). However, no matter have many threads are requested, the final number if always capped to 31 (ThreadPool::MaxWorkers). You number of available threads for you hardware can be retrived via ThreadPool::hw_thread_cnt.

auto& tp = mt::ThreadPool::get();

// Get the number of hardware threads.
const uint32_t hwThreads = tp.hw_thread_cnt();

// Create "hwThreads" worker threads. Make all of them high priority workers.
tp.set_max_workers(hwThreads, hwThreads);

// Create "hwThreads" worker threads. Make 1 of them a high priority worker.
// If more then 1 worker threads were created, the rest of them is dedicated
// for low-priority jobs.
tp.set_max_workers(hwThreads, 1);

// No workers threads are used. If there were any before, they are destroyed.
// All processing is happening on the main thread.
tp.set_max_workers(0, 0);

The number of worker threads for a given performance level can be adjusted via ThreadPool::set_workers_high_prio and ThreadPool::set_workers_low_prio. By default, all workers created are high-priority ones.

auto& tp = mt::ThreadPool::get();

// Get the number of worker threads available for this system.
const uint32_t workers = tp.workers();

// Set the number of worker threads dedicted for performance cores.
// E.g. if workers==5, this dedicates 4 worker threads for high-performance workloads
// and turns the remaining 1 into an efficiency worker.
tp.set_workers_high_prio(workers - 1);

// Make all workers high-performance ones.
tp.set_workers_high_prio(workers);

// Set the number of worker threads dedicted for efficiency cores.
// E.g. if workers==5, this dedicates 4 worker threads for efficiency workloads loads
// and turns the remaining 1 into a high-performance worker.
tp.set_workers_low_prio(workers - 1);

// Make all workers low-performance ones.
tp.set_workers_low_prio(workers);

The main thread normally does not participate as a worker thread. However, if needed, it can join workers by calling ThreadPool::update from the main thread.

auto& tp = mt::ThreadPool::get();

ecs::World w1, w2;
while (!stopProgram) {
  // Create many jobs here
  // ...

  // Update both worlds
  w1.update();
  w2.update();

  // Help execute jobs on the main thread, too
  tp.update();
}

If you need to designate a certain thread as the main thread, you can do it by calling ThreadPool::make_main_thread from that thread.

Note, the operating system has the last word here. It might decide to schedule low-priority threads to high-performance cores or high-priority threads to efficiency cores depending on how the scheduler decides it should be.

Release notes

Fixed:

  • hashmap element cloning 70d38e6
  • archetype graph not properly cleaned when deleting entities b68b668
  • adding an entity to another entity would not create a new archetype 6bbe350
  • entity deletion safety 6a23a22
  • ecs::pair would not be able to discern gen & uni entities 5b8efde
  • duplicates archetype matches in cache after a new archetype has been added in between queries 92198c5
  • accessing released memory possible when deleting chunks b1abdd5
  • make compile-time type-info strings match on supported compilers c634eb8

Changed/Tweaked:

  • use direct hashing for id->archetype maps b19a090
  • improved component diags 254abed

Added:

Read more

v0.8.2

16 Dec 06:10
9df49b3
Compare
Choose a tag to compare

This is a maintenance release with a few small new features at the top.

Most of fixes and tweaks are related to CMake and Docker.

Iteration benchmark was rewritten so it does not generate thousands of various compile-time recursive operations to improve compilation times and ease of use (80s along with linking down to insignificant amount of time).

Also, starting with this release, sponsorship has became available:
https://github.com/sponsors/richardbiely
Therefore, if you like the project and want to support it, give it a star or became a sponsor. Thank you everyone, you rock!

Entity dependencies

Defining dependencies among entities is made possible via the (DependsOn, target) relationship.

When adding an entity with a dependency to some source it is guaranteed the dependency will always be present on the source as well. It will also be impossible to delete it.

ecs::World w;
ecs::Entity rabbit = w.add();
ecs::Entity animal = w.add();
ecs::Entity herbivore = w.add();
ecs::Entity carrot = w.add();
w.add(carrot, ecs::Pair(ecs::DependsOn, herbivore));
w.add(herbivore, ecs::Pair(ecs::DependsOn, animal));

// carrot depends on herbivore so the later is added as well.
// At the same time, herbivore depends on animal so animal is added, too.
w.add(rabbit, carrot);
const bool isHerbivore = w.has(rabbit, herbivore)); // true
const bool isAnimal = w.has(rabbit, animal); // true

// Animal will not be removed from rabbit because of the dependency chain.
// Carrot depends on herbivore which depends on animal.
w.del(rabbit, animal); // does nothing
// Herbivore will not be removed from rabbit because of the dependency chain.
// Carrot depends on herbivore.
w.del(rabbit, herbivore); // does nothing

// Carrot can be deleted. It requires herbivore is present which is true.
w.del(rabbit, carrot); // removes carrot from rabbit

Entity constraints

Entity constrains are used to define what entities cannot be combined with others. Constraints are defined via the (DependsOn, target) relationship.

ecs::World w;
ecs::Entity weak = w.add();
ecs::Entity strong = w.add();
w.add(weak, ecs::Pair(ecs::CantCombine, strong));

ecs::Entity e = w.add();
w.add(e, strong);
// This is an invalid operation.
w.add(e, weak);

GAIA_DEVMODE

Developer mode is a macro that enables more integrity checks for various internal operations. Some of them are expensive and almost redundant but keep the code safe from regressions and various hard-to-locate bugs. However, they are not really useful for anyone but library maintainers. Therefore, they are now enabled only when GAIA_DEVMODE macro is on:

#define GAIA_DEVMODE 1
#include <gaia.h>

Release notes

Fixed:

  • World::relation(s) not returning proper entities 826043d

  • wildcard relationship deletion - 30af94d

  • certain CMake settings not reflected in generate_compile_commands target dcf6c58

  • crash when accessing EntityDesc of a pair (only regular entities have it) 30af94d

  • crash when copying an entity with entities attached to it 12757c7

  • crash when using diagnostics with an entity without the EntityDesc 5b145e0

  • asserts handling with GAIA_DISABLE_ASSERTS 5a2aef1

  • uninitialized variables on Archetype 8e938db

  • darr_ext move constructor ed99fd6

  • non-Xcode build workaround for MacOS with Xcode 15 or later installed 83a1a33

  • benchmark CMake configuration 689cabb

  • string argument expansion for Docker builds 689cabb

  • the correct compiler is used always for Docker builds 79717d3

Changed:

  • OnDeleteTarget behavior changed fbe6e10
  • "optional" for string queries uses ? instead of + now 2dc3b8e
  • IteratorXYZ renamed to IterXYZ 63a53af
  • handle serialization asserts at compile-time to avoid issues with older compilers 7075135
  • picobench for benchmarking made an external CMake dependency 26687dd
  • Catch2 updated to 3.5.0 10564cf
  • iIteration benchmark updated ccb8bf0 d9ab565

Tweaked:

  • improved query matching performance for simple queries 930e86f
  • one less hash combination when calculating the query hash 59b6a11
  • QOL improvement for IterAll 539d000

Added:

  • support for Pair(DependsOn, X) 27ff060
  • support for Pair(CantCombine, X) 28128a7 5a2aef1
  • GAIA_DEVMODE for library maintainers 63d7226
  • more configuration options to CMake 689cabb

Full Changelog: v0.8.1...v0.8.2

v0.8.1

04 Dec 19:34
04475c1
Compare
Choose a tag to compare

This is a critical fix addressing wrongly sorted internal Query data structures.
Additionally, it changes how data is matched. User-created queries will no longer match special core components.

A new cleanup rule was added that tells the framework a certain entity is not allowed to be deleted. They can be used by adding a relationship pair (OnDelete, Error) and/or (OnDeleteTarget, Error) to an entity.

ecs::World w;
ecs::Entity rabbit = w.add();
ecs::Entity eats = w.add();
ecs::Entity carrot = w.add();
w.add(rabbit, ecs::Pair(eats, carrot));

w.add(carrot, ecs::Pair(OnDelete, Error));
w.del(carrot); // This asserts and reports errors!

w.add(carrot, ecs::Pair(OnDeleteTarget, Error));
w.del(rabbit, ecs::Pair(eats, carrot)); // This asserts and reports errors!

Release notes

Fixed:

  • Wrong query items ordering 7d99342

Changed:

  • Changed: Core components can't be deleted 04475c1
  • Changed: Special core components won't be matched by user queries 04475c1

Added:

  • (OnDelete[Target], Error) to prevent entities from getting deleted 04475c1

Full Changelog: v0.8.0...v0.8.1