Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Breaking API changes #466

SanderMertens started this conversation in Announcements
Aug 11, 2021 · 90 comments · 2 replies
Discussion options

A discussion for listing all breaking API changes.

You must be logged in to vote

Replies: 90 comments · 2 replies

Comment options

The first branch with breaking changes is about to merge with master. This branch mostly just removes deprecated functions and features, which includes all functions that were marked as deprecated in the last release. Some of the removed functionality (such as bulk APIs) will be reintroduced at a later point, but with a different API design. The highlights are:

  • It is no longer possible to add/remove ecs_type_t's to an entity
  • Replace legacy filters (include, exclude) with new term-based API
  • Remove reader/writer, bulk API, queue, debug API & direct access addons
  • Removed all trait-related APIs (use pairs/relations instead)
  • Scope iterators have been removed (use term/filter iterators instead)
  • ECS_OWNED/flecs::Owned has been replaced with ECS_OVERRIDE/flecs::Override

C++ API

  • Remove pod_component, relocatable_component
  • Remove entity_range
  • Remove action() iterate function
  • Add a separate class for triggers (vs. it being lumped together with system)
  • Removal of add_childof, add_instanceof (use child_of, is_a)
  • children() now accepts a callback function vs. returning an iterator
  • The module constructor no longer has the ability to provide a custom name

Some improvements have been made to code organization:

  • All C convenience macro's are now in a separate addons/flecs_c.h header
  • The C++ API is now a regular addon
  • Modules are now regular addons
  • All datastructure code has been moved to a separate folder
You must be logged in to vote
0 replies
Comment options

More things have been updated before the first merge:

  • Support for the old query syntax is now entirely removed. See below for a comparison between old/new style
  • "superset" and "subset" have been truncated to "super" and "sub" in the query language
  • A "parent" modifier has been added which is a shortcut for superset(ChildOf)
  • Monitor systems are removed from the core and have been replaced with Observers. See below for the replacement.
  • Observers can now be created with a Monitor tag which generate a single event when an entity matches/unmatches
  • Filters now no longer match disabled/prefab entities, unless they have a Disabled/Prefab term
  • Terms now no longer match disabled/prefab entities, unless explicitly enabled with match_prefab/disabled
  • Triggers (and observers) now support terms with superset modifiers
  • Triggers (and observers) now support terms with a Not operator
  • The .remove field from ecs_entity_desc_t has been removed
  • Removed deprecated NOT, OR, XOR type constraints
  • The ecs_query_iter function now accepts a world
  • Triggers (and observers) can now trigger on custom events, and can specify event wildcards (beta)
  • Custom events can be thrown with a new ecs_emit function (beta)

Here are a few examples of the old query syntax vs. the new query syntax:

// old
PARENT:Position
// new
Position(parent)
// old
CASCADE:Position
// new - note the '?', this makes the term optional so that it also matches root entities, which was implicit with CASCADE
?Position(parent|cascade)
// old
Foo:Position
// new
Position(Foo)
// old
:Position
// new
Position()
// old
[out] :*
// new
[out] *()
// old
ANY:Position
// new
Position(self|super)
// old
OWNED:Position
// new
Position(self)
// old
SHARED:Position
// new
Position(super)
// old
OWNED:Likes FOR *
// or
OWNED:TRAIT | Likes > *
// new
Likes(self, *)

Monitor systems are no longer supported and are replaced with monitor observers:

// old
world.system<Position, Velocity>()
  .kind(flecs::Monitor)
  .each([](flecs::iter it) {
    // triggers when entity enters the condition
  });

// new
world.observer<Position, Velocity>()
  .event(flecs::Monitor)
  .each([](flecs::iter it) {
    if (it.event() == flecs::OnAdd) {
      // entity enters condition (matches Position, Velocity for the first time)
    } else {
      // entity exits the condition (no longer matches Position, Velocity)
    }
  });

OnSet systems are no longer supported and are replaced with OnSet observers:

// old
world.system<Position, Velocity>()
  .kind(flecs::OnSet)
  .each([](flecs::iter it) {
    // triggers when entity sets Position or Velocity and has both
  });

// new
world.observer<Position, Velocity>()
  .event(flecs::OnSet)
  .each([](flecs::iter it) {
    // triggers when entity sets Position or Velocity and has both
  });
You must be logged in to vote
2 replies
@logankaser
Comment options

Was "// entity exists the condition" supposed to be "// entity exits the condition" or "// entity exists with the condition"?

@SanderMertens
Comment options

SanderMertens Oct 17, 2021
Maintainer Author

@logankaser That should've been exits, fixed!

Comment options

A few breaking changes were introduced for the C++ API:

  • checking whether an optional term was set should now be done using the flecs::iter::is_set function (the flecs::column::is_set method was removed)
  • Checking if a column is owned should use the flecs::iter::is_owned method (the flecs::column::is_owned / flecs::column::is_shared methods were removed)
You must be logged in to vote
0 replies
Comment options

Breaking changes were introduced to the C & C++ query APIs:

  • The C++ term_builder::subject and term_builder::object methods are renamed to subj and obj
  • The C term::args argument has been split into term::subj (previously term::args[0]) and obj (previously term::args[1])
  • The world_time member has been removed from ecs_iter_t and flecs::iter. Use ecs_get_world_info instead
// Old (applies to anything that uses `ecs_term_t`, such as `ecs_filter_desc_t` and `ecs_query_desc_t`)
ecs_iter_t it = ecs_filter_init(world, &(ecs_term_t) {
  .args[0].set.mask = EcsSuperSet
});

// New
ecs_iter_t it = ecs_filter_init(world, &(ecs_term_t) {
  .subj.set.mask = EcsSuperSet
});
// Old
auto q = world.query_builder<>()
  .term<Position>().subject().super()
  .build();

// New
auto q = world.query_builder<>()
  .term<Position>().subj().super()
  .build();
You must be logged in to vote
0 replies
Comment options

The C module API has been revised to reduce module code boilerplate. The module struct and ImportHandles macro are no longer required. The module system is now designed around global component identifiers, which eliminates the need for passing component ids around, and improves interoperability between worlds.

This example shows the difference between the old and new API:

// Old module header
typedef struct {
  float x;
  float y;
} Position;

typedef struct {
  ECS_DECLARE_COMPONENT(Position);
} MyModule;

void MyModuleImport(ecs_world_t *world);

#define MyModuleImportHandles(handles)\
    ECS_IMPORT_COMPONENT(handles, Position);


// Old module source
void MyModuleImport(ecs_world_t *world) {
  ECS_MODULE(world, MyModule);

  ECS_COMPONENT(world, Position);

  ECS_EXPORT_COMPONENT(Position);
}
// New module header
typedef struct {
  float x;
  float y;
} Position;

extern ECS_COMPONENT_DECLARE(Position);

void MyModuleImport(ecs_world_t *world);


// New module source
ECS_COMPONENT_DECLARE(Position);

void MyModuleImport(ecs_world_t *world) {
  ECS_MODULE(world, MyModule);

  ECS_COMPONENT_DEFINE(world, Position);
}
You must be logged in to vote
0 replies
Comment options

Breaking changes have been introduced to the logging callbacks of the OS API. Additionally the logging API is now an addon.

The log_* functions in the OS API have been replaced with a single function that accepts a logging level, file, line and message. The function no longer accepts a variable argument list.

A number of changes have been made to the logging API that make it more consistent

  • ecs_enable_tracing is now called ecs_log_set_level
  • ecs_tracing_color_enable is now called ecs_log_enable_colors
  • The logging levels have changed to differentiate between tracing and debugging:
    • level >0 = debug
    • level 0 = trace
    • level -2 = warning
    • level -3 = error
    • level -4 = fatal

Level -1 is unused on purpose, as this leaves open the possibility of returning a log level from a function, while still using -1 as the default "this operation has failed" return code.

The following changes have been made to the API:

  • There is now a single ecs_log function that accepts a level
  • The API has macro's for debug, tracing, warning, error and fatal that call ecs_log
  • The API is now an addon. Disabling the addon reduces footprint but also removes tracing & most error reporting
  • When the logging addon is disabled, asserts & aborts still work

The messages passed to the OS API have changed:

  • They no longer include the log level (this is available as a parameter to the log function)
  • They no longer include the indentation (indentation value is stored on the logging API struct)
  • They no longer include file and line number (these are available as parameters passed to the function)
You must be logged in to vote
0 replies
Comment options

Queries with a case term now need to provide both the switch and the case in a term:

// Old
ecs_query_new(world, "CASE | Walking");
// New
ecs_query_new(world, "CASE | (Movement, Walking)");
// Old
ecs_query_init(world, &(ecs_query_desc_t) {
  .filter.terms = {{ .id = ECS_CASE | Walking }}
});
// New
ecs_query_init(world, &(ecs_query_desc_t) {
  .filter.terms = {{ .id = ecs_case(Movement, Walking) }}
});
// Old
world.query_builder<>()
  .term<Movement::Walking>().role(flecs::Case);
// New
world.query_builder<>()
  .term<Movement, Movement::Walking>().role(flecs::Case);

This change was introduced to support creating triggers/observers for terms that match a case, as they need to register themselves for the corresponding switch, which before this change was not known.

The change also brings the switch/case feature closer to pairs. Both features will be merged in the upcoming storage redesign.

You must be logged in to vote
0 replies
Comment options

The flecs::prefab type no longer exists. Prefabs are now stored as regular entities (with the flecs::entity type). Prefabs can still be created with world.prefab(), which returns a flecs::entity with the Prefab tag.

You must be logged in to vote
0 replies
Comment options

The C++ API to associate entities/types with a C++ type has changed:

struct Foo { };

// Old
world.entity().component<Foo>();
world.prefab().component<Foo>();
world.type().component<Foo>();

// New
world.entity<Foo>();
world.prefab<Foo>();
world.type<Foo>();
You must be logged in to vote
0 replies
Comment options

The ecs_query_page_iter and ecs_query_next_worker features have been replaced by chained page/worker iterators that can be used with any source iterator.

Page iterator:

// Old
ecs_iter_t it = ecs_query_page_iter(world, q, 10, 20); // offset 10, limit 20
while (ecs_page_next(&pit)) {
  // iterate as usual
}

// New
ecs_iter_t it = ecs_query_iter(world, q);
ecs_iter_t pit = ecs_page_iter(&it, 10, 20); // offset 10, limit 20
while (ecs_page_next(&pit)) {
  // iterate as usual
}

Worker iterator:

// Old
ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next_worker(&pit, 0, 2)) {  // worker id 0, worker count 2
  // iterate as usual
}

// New
ecs_iter_t it = ecs_query_iter(world, q);
ecs_iter_t wit = ecs_worker_iter(&it, 0, 2); // worker id 0, worker count 2
while (ecs_worker_next(&wit)) {
  // iterate as usual
}
You must be logged in to vote
0 replies
Comment options

When parsing query identifiers, single upper-case letters are no longer treated as variables. All variables must now be prefixed with _.

// Old
ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t) {
    .expr = "Likes(X, Y)"
});

int32_t x = ecs_rule_find_variable(r, "X");
int32_t y = ecs_rule_find_variable(r, "Y");
// New
ecs_rule_t *r = ecs_rule_init(world, &(ecs_filter_desc_t) {
    .expr = "Likes(_X, _Y)"
});

int32_t x = ecs_rule_find_variable(r, "X");
int32_t y = ecs_rule_find_variable(r, "Y");
You must be logged in to vote
0 replies
Comment options

The on_set callback signature (as set in the EcsComponentLifecycle struct) has been changed to match that of iterators (void(*)(ecs_iter_t *).

You must be logged in to vote
0 replies
Comment options

The ecs_type_index_of and ecs_type_has_id functions have been replaced by ecs_search, ecs_search_offset and ecs_search_relation.

You must be logged in to vote
0 replies
Comment options

A few methods of the flecs::iter class have been renamed:

  • flecs::iter::term_id is renamed to flecs::iter::id
  • flecs::iter::term_source is renamed to flecs::iter::source

Additionally, tag arguments (empty types) can no longer be passed as references to each, which addresses a UB issue. The following code will now no longer compile:

struct Tag { };

auto q = world.query<Tag>();

q.each([](flecs::entity e, Tag&) { }); // Illegal, cannot be a reference

The correct way to add the tag argument is as a regular value:

struct Tag { };

auto q = world.query<Tag>();

q.each([](flecs::entity e, Tag) { }); // Ok

Passing a tag as argument doesn't have much added value though, so another alternative is to do this:

struct Tag { };

auto q = world.query_builder().term<Tag>().build();

q.each([](flecs::entity e) { }); // Ok
You must be logged in to vote
0 replies
Comment options

The C++ flecs::map wrapper has been removed. The reason is that it is a datastructure optimized for the usage in the library, and does not have features for things general purpose should contain, such as resource management.

You must be logged in to vote
0 replies
Comment options

Member target queries no longer return field data, and instead return the currently iterated member value as field id:

// Old
ecs_query_t *q = ecs_query(world, {
  .expr = "(Movement.value, *)"
});

ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
  ecs_entity_t *tgts = ecs_field(&it, Position, 0);
  for (int i = 0; i < it.count; i ++) {
    ecs_entity_t tgt = tgts[i];
  }
}
// New
ecs_query_t *q = ecs_query(world, {
  .expr = "(Movement.value, *)"
});

ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
  ecs_entity_t tgt = ecs_pair_second(world, ecs_field_id(&it, 0));
  for (int i = 0; i < it.count; i ++) {
  }
}

In C++:

// Old
auto q = world.query_builder()
  .expr("(Movement.value, *)")
  .each([](flecs::iter& it, size_t row) {
    flecs::entity_t tgt = it.field_at<flecs::entity_t>(0);
  });
// New
auto q = world.query_builder()
  .expr("(Movement.value, *)")
  .each([](flecs::iter& it, size_t row) {
    flecs::entity tgt = it.pair(0).second();
  });
You must be logged in to vote
0 replies
Comment options

The location of the flecs.c and flecs.h files have been changed to a new distr folder. This folder is configured with a new "amalgamate-path" setting in project.json which requires the latest version of bake.

You must be logged in to vote
0 replies
Comment options

ecs_get_path_w_sep_buf now has an additional escape argument that will escape special characters.

You must be logged in to vote
0 replies
Comment options

The following changes have been made to the Flecs script API:

  • ecs_script_parse now accepts an additional ecs_script_eval_desc_t desc argument
  • ecs_script_eval now accepts an additional ecs_script_eval_desc_t desc argument
  • ecs_script_ast_to_buf and ecs_script_ast_to_string have an additional colors argument
  • ecs_script_expr_run has been renamed to ecs_expr_run
You must be logged in to vote
0 replies
Comment options

The flecs::reset() function has been removed. Component ids are now local to a world, so this function is no longer necessary.

You must be logged in to vote
0 replies
Comment options

Events for communicating that a table has become empty/non-empty no longer exist. This means that queries now have to skip empty tables during iteration, which can impact iteration speed if an application has lots of empty tables. To remove this overhead an application can periodically call ecs_delete_empty_tables, which now also has a new signature:

ecs_delete_empty_tables(world, &(ecs_delete_empty_tables_desc_t) {
  .delete_generation = 2 // delete empty tables if it has been empty for 2 calls to ecs_delete_empty_tables
});

This change moves overhead from difficult to control locations (emitting empty/non-empty events, updating table & query caches when they happen) to a single configurable and optional function call.

The following event id constants no longer exist:

  • EcsOnTableEmpty
  • EcsOnTableFill

The following flag (used for ecs_run_aperiodic) no longer exists:

  • EcsAperiodicEmptyTables
You must be logged in to vote
0 replies
Comment options

The ecs_http_server_request function now has an additional body parameter:

ecs_http_server_request(srv, "PUT", "/script", "e = {}" /* new */, &reply);
You must be logged in to vote
0 replies
Comment options

The const var = value notation has been deprecated in Flecs script and support for it will be removed in version 4.0.5:

// Old
const x = 10
// New
const x: 10

This change fixes an inconsistency issue:

// identifier = value
const x = 10

// identifier = type: value
const x = i32: 10

struct Position {
  x = f32 // identifier = type
  y = f32
}

e {
  // type: value
  Position: {10, 20}
}

In the new syntax, a : is always followed by a value, and a = is always followed by a type.

You must be logged in to vote
0 replies
Comment options

An empty scope in Flecs script now creates an anonymous entity. The old notation is still supported:

// This ...
_ {
  Position: {10, 20}
}

// ... is now the same as this:
{
  Position: {10, 20}
}
You must be logged in to vote
0 replies
Comment options

The flecs.meta.constant entity has moved to flecs.core. Applications that rely on looking up flecs.meta.constant by name (including from string-based representations like the query DSL) will have to be updated to the new location.

The entity moved to the core to allow for enum reflection without having to enable the FLECS_META addon.

You must be logged in to vote
0 replies
Comment options

Entities without components are now stored in a table without components. This has the following advantages:

  • entities without components can now be matched by queries
  • entities without components can trigger observers
  • cleanup logic correctly takes into account entities without components

This introduces a breaking changes:

  • ecs_get_type / entity.type() would previously return NULL when an entity had no components, now it returns an ecs_type_t with 0 elements.
  • same for ecs_get_table / entity.table()
You must be logged in to vote
0 replies
Comment options

Specifying a component using the query builder with .with now uses the InOutDefault inout kind (previously InOutNone). For most scenarios this should not cause existing code to break, however this scenario will now throw an assert:

auto q = world.query_builder()
  .with<Position>().src(e)
  .build();

q.run([](flecs::iter& it) {
  while (it.next()) {
    auto p = it.field<Position>(0); // access violation: component is readonly
  }
});

This happens because with InOutDefault, terms with a non-$this source automatically become readonly. To fix the assert either add .inout() to the builder:

auto q = world.query_builder()
  .with<Position>().src(e).inout()
  .build();

or add const to the code fetching the field:

q.run([](flecs::iter& it) {
  while (it.next()) {
    auto p = it.field<const Position>(0);
  }
});
You must be logged in to vote
0 replies
Comment options

The flecs::entity::remove method had an issue where if it was used with an enum type, it would remove an enum relationship. This prevented the function from being used to remove enum components. To remove an enum relationship, the function now can be called with a Wildcard argument:

// Old
enum Color {
  Red, Green, Blue
};

e.add(Color::Red); // adds (Color, Red)
e.remove<Color>(); // removes (Color, *)

e.set<Color>(Red); // adds Color { Red }
e.remove<Color>(); // doesn't remove Color
// New
enum Color {
  Red, Green, Blue
};

e.add(Color::Red); // adds (Color, Red)
e.remove<Color>(flecs::Wildcard) // removes (Color, *)

e.set<Color>(Red); // adds Color { Red }
e.remove<Color>(); // removes Color
You must be logged in to vote
0 replies
Comment options

Change detection now has to be explicitly enabled on the queries that are used to detect changes. When change detection is not enabled, calling ecs_query_changed or ecs_iter_changed will fail.

// C
ecs_query_t *q = ecs_query(world, {
  .terms = { ... },
  .flags = EcsQueryDetectChanges
});
// C++
flecs::query<...> q = world.query_builder<...>()
  .detect_changes()
  .build();
You must be logged in to vote
0 replies
Comment options

It is no longer valid to pass 0 to the size argument of ecs_field_w_size. A valid size must now be passed into the function, or the code will assert. If the size is not known at compile time, code can use ecs_field_size:

// Old
void *ptr = ecs_field_w_size(&it, 0, 1);
// New
void *ptr = ecs_field_w_size(&it, ecs_field_size(&it, 0), 1);
You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.