From a255d25ada3a8684f874dda6ed587083f5f6c5c6 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Tue, 18 Nov 2025 21:02:39 +0100 Subject: [PATCH] New solver and Query:has --- addons/ob.luau | 95 +++-------- jecs.luau | 392 ++++++++++++++++++++++++++++++-------------- test/addons/ob.luau | 1 + 3 files changed, 285 insertions(+), 203 deletions(-) diff --git a/addons/ob.luau b/addons/ob.luau index 5a01118..6149e65 100755 --- a/addons/ob.luau +++ b/addons/ob.luau @@ -13,8 +13,8 @@ export type Observer = { disconnect: (Observer) -> (), } -export type Monitor = { - disconnect: (Observer) -> (), +export type Monitor = { + disconnect: () -> (), added: ((jecs.Entity) -> ()) -> (), removed: ((jecs.Entity) -> ()) -> () } @@ -23,37 +23,19 @@ local function observers_new( query: Query, callback: (jecs.Entity) -> () ): Observer - query:cached() + local cachedquery = query:cached() - - local world = (query :: Query & { world: World }).world + local world = (cachedquery :: Query & { world: World }).world callback = callback - local archetypes = {} - local terms = query.filter_with :: { jecs.Id } - local first = terms[1] + local archetypes = cachedquery.archetypes_map + local terms = query.filter_with :: { jecs.Id } - local observers_on_create = world.observable[jecs.ArchetypeCreate][first] - local observer_on_create = observers_on_create[#observers_on_create] - observer_on_create.callback = function(archetype) - archetypes[archetype.id] = true - end - local observers_on_delete = world.observable[jecs.ArchetypeDelete][first] - local observer_on_delete = observers_on_delete[#observers_on_delete] - observer_on_delete.callback = function(archetype) - archetypes[archetype.id] = nil - end - - local entity_index = world.entity_index :: any - - for _, archetype in query:archetypes() do - archetypes[archetype.id] = true - end + local entity_index = world.entity_index local function emplaced( entity: jecs.Entity, - id: jecs.Id, - value: a + id: jecs.Id ) local r = entity_index.sparse_array[jecs.ECS_ID(entity)] @@ -130,18 +112,6 @@ local function observers_new( end local function disconnect() - table.remove(observers_on_create, table.find( - observers_on_create, - observer_on_create - )) - - table.remove(observers_on_delete, table.find( - observers_on_delete, - observer_on_delete - )) - - table.clear(archetypes) - for _, disconnect in cleanup do disconnect() end @@ -154,38 +124,22 @@ local function observers_new( return observer end -local function monitors_new(query: Query): Monitor - query:cached() +local function monitors_new(query: Query<...any>): Monitor + local cachedquery = query:cached() - local world = (query :: Query & { world: World }).world + local world = (cachedquery :: Query<...any> & { world: World }).world :: jecs.World - local archetypes = {} - local terms = query.filter_with :: { jecs.Id } - local first = terms[1] + local archetypes = cachedquery.archetypes_map + local terms = cachedquery.filter_with :: { jecs.Id } - local observers_on_create = world.observable[jecs.ArchetypeCreate][first] - local observer_on_create = observers_on_create[#observers_on_create] - observer_on_create.callback = function(archetype) - archetypes[archetype.id] = true - end - local observers_on_delete = world.observable[jecs.ArchetypeDelete][first] - local observer_on_delete = observers_on_delete[#observers_on_delete] - observer_on_delete.callback = function(archetype) - archetypes[archetype.id] = nil - end - - for _, archetype in query:archetypes() do - archetypes[archetype.id] = true - end local entity_index = world.entity_index :: any local callback_added: ((jecs.Entity) -> ())? local callback_removed: ((jecs.Entity) -> ())? - local function emplaced( - entity: jecs.Entity, - id: jecs.Id, - value: a + local function emplaced( + entity: jecs.Entity, + id: jecs.Id ) if callback_added == nil then return @@ -201,7 +155,7 @@ local function monitors_new(query: Query): Monitor end end - local function removed(entity: jecs.Entity, component: jecs.Id) + local function removed(entity: jecs.Entity, component) if callback_removed == nil then return end @@ -259,6 +213,7 @@ local function monitors_new(query: Query): Monitor table.insert(cleanup, onadded) table.insert(cleanup, onremoved) end + end local without = query.filter_without @@ -336,18 +291,6 @@ local function monitors_new(query: Query): Monitor end local function disconnect() - table.remove(observers_on_create, table.find( - observers_on_create, - observer_on_create - )) - - table.remove(observers_on_delete, table.find( - observers_on_delete, - observer_on_delete - )) - - table.clear(archetypes) - for _, disconnect in cleanup do disconnect() end @@ -365,7 +308,7 @@ local function monitors_new(query: Query): Monitor disconnect = disconnect, added = monitor_added, removed = monitor_removed - } :: Monitor + } :: Monitor return monitor end diff --git a/jecs.luau b/jecs.luau index aab0dcb..3f82601 100755 --- a/jecs.luau +++ b/jecs.luau @@ -19,35 +19,118 @@ export type Archetype = { type: string, entities: { Entity }, columns: { Column }, - columns_map: { [Id]: Column } + columns_map: { [Component]: Column } } export type QueryInner = { compatible_archetypes: { Archetype }, - ids: { Id }, - filter_with: { Id }, - filter_without: { Id }, + archetypes_map: { [number]: number }, + ids: { Component }, + filter_with: { Component }, + filter_without: { Component }, next: () -> (Entity, ...any), world: World, } -export type Entity ={ __T: T } -export type Id = { __T: T } -export type Pair = Id

-type ecs_id_t = Id | Pair | Pair<"Tag", T> +type function ecs_entity_t(ty: type) + if ty:is("union") then + for _, component in ty:components() do + assert(not component:readproperty(types.singleton("__IS_PAIR")), "Expected Entity got Pair") + end + end + assert(ty:readproperty(types.singleton("__T")), "Expected Entity") + assert(not ty:readproperty(types.singleton("__IS_PAIR")), "Expected Entity got Pair") + return ty +end + +type function ecs_pair_t(first: type, second: type) + local __T = types.singleton("__T") + local __IS_PAIR = types.singleton("__IS_PAIR") + + local first_t = first:readproperty(__T) + local second_t = second:readproperty(__T) + + assert(first_t and second_t, "Expected at least one Entity in pair") + + local id = types.newtable() + local ty = if first_t:is("nil") then second_t else first_t + + id:setproperty(__T, ty) + id:setproperty(__IS_PAIR, types.singleton(true)) + + return id +end + +type function ecs_id_t(first: type, second: type) + local __T = types.singleton("__T") + if second:is("nil") then + local p = ecs_pair_t(Entity(first), Entity(second)) + + -- Create component type that matches Component structure exactly + -- This should be structurally compatible with Component + local component_type = types.newtable() + component_type:setproperty(__T, first) + + -- Union order: component first, then pair + -- This helps Luau recognize Component as assignable + -- local u = types.unionof(component_type, p) + -- return u + return component_type + end + + local function entity(ty: type) + local e = types.newtable() + e:setproperty(__T, ty) + return e + end + + local e1 = entity(first) + local e2 = entity(second) + return ecs_pair_t(e1, e2) +end + +export type Entity = { __T: T } +export type Id2 = { __T: T } +export type Pair = ecs_pair_t, Entity> +export type Component = { __T: T } +export type Id = ecs_id_t + export type Item = (self: Query) -> (Entity, T...) export type Iter = (query: Query) -> () -> (Entity, T...) +export type CachedIter = (query: CachedQuery) -> () -> (Entity, T...) + +type TypePack = { + __phantomdata: () -> (T...) +} + +export type CachedQuery = typeof(setmetatable( + {} :: { + iter: CachedIter, + archetypes: (self: CachedQuery) -> { Archetype }, + cached: (self: CachedQuery) -> CachedQuery, + has: (CachedQuery, Entity) -> boolean, + ids: { Id }, + filter_with: { Id }?, + filter_without: { Id }?, + archetypes_map: { [number]: number }, + -- world: World + }, + {} :: { + __iter: CachedIter, + }) +) export type Query = typeof(setmetatable( {} :: { iter: Iter, - with: ((Query, ...Id) -> Query), - without: ((Query, ...Id) -> Query), + with: ((Query, ...Component) -> Query), + without: ((Query, ...Component) -> Query), archetypes: (self: Query) -> { Archetype }, - cached: (self: Query) -> Query, + cached: (self: Query) -> CachedQuery, + has: (Query, Entity) -> boolean, ids: { Id }, - filter_with: { Id }?, - filter_without: { Id }? + filter_with: { Id }?, + filter_without: { Id }?, -- world: World }, {} :: { @@ -148,6 +231,7 @@ type world = { removed: (world, i53, (e: i53, id: i53) -> ()) -> () -> (), } + export type World = { archetype_edges: Map>, archetype_index: { [string]: Archetype }, @@ -159,48 +243,57 @@ export type World = { max_component_id: number, max_archetype_id: number, - observable: Map>, + observable: Map>, - added: (World, Entity, (e: Entity, id: Id, value: T, oldarchetype: Archetype) -> ()) -> () -> (), - removed: (World, Entity, (e: Entity, id: Id) -> ()) -> () -> (), - changed: (World, Entity, (e: Entity, id: Id, value: T, oldarchetype: Archetype) -> ()) -> () -> (), + added: (World, Component, (e: Entity, id: Id, value: T, oldarchetype: Archetype) -> ()) -> () -> (), + removed: (World, Component, (e: Entity, id: Id) -> ()) -> () -> (), + changed: (World, Component, (e: Entity, id: Id, value: T, oldarchetype: Archetype) -> ()) -> () -> (), --- Enforce a check on entities to be created within desired range range: (self: World, range_begin: number, range_end: number?) -> (), --- Creates a new entity - entity: (self: World, id: (number | Entity)?) -> Entity, + entity: + & ((self: World) -> Entity) + & ((self: World, id: Entity) -> Entity) + & ((self: World, id: number) -> Entity) + & ((self: World, id: Component) -> Component), --- Creates a new entity located in the first 256 ids. --- These should be used for static components for fast access. component: (self: World) -> Entity, --- Gets the target of an relationship. For example, when a user calls --- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity. - target: (self: World, id: Entity, relation: Id, index: number?) -> Entity?, + target: (self: World, id: Entity, relation: ecs_entity_t, index: number?) -> Entity?, --- Deletes an entity and all it's related components and relationships. delete: (self: World, id: Entity) -> (), --- Adds a component to the entity with no value - add: (self: World, id: Entity, component: Id) -> (), + add: ( + self: World, + id: ecs_entity_t, + component: Component + ) -> (), + --- Assigns a value to a component on the given entity - set: (self: World, id: Entity, component: Id, data: a) -> (), + set: (self: World, id: Entity, component: Component, data: a) -> (), cleanup: (self: World) -> (), -- Removes all components from the entity clear: (self: World, entity: Entity) -> (), --- Removes a component from the given entity - remove: (self: World, id: Entity, component: Id) -> (), + remove: (self: World, id: Entity, component: Component) -> (), --- Retrieves the value of up to 4 components. These values may be nil. - get: & ((World, Entity, Id) -> a?) - & ((World, Entity, Id, Id) -> (a?, b?)) - & ((World, Entity, Id, Id, Id) -> (a?, b?, c?)) - & ((World, Entity, Id, Id, Id, Id) -> (a?, b?, c?, d?)), + get: & ((World, Entity, Component) -> a?) + & ((World, Entity, Component, Component) -> (a?, b?)) + & ((World, Entity, Component, Component, Component) -> (a?, b?, c?)) + & ((World, Entity, Component, Component, Component, Component) -> (a?, b?, c?, d?)), --- Returns whether the entity has the ID. - has: ((World, Entity, Id) -> boolean) - & ((World, Entity, Id, Id) -> boolean) - & ((World, Entity, Id, Id, Id) -> boolean) - & (World, Entity, Id, Id, Id, Id) -> boolean, + has: ((World, Entity, Component) -> boolean) + & ((World, Entity, Component, Component) -> boolean) + & ((World, Entity, Component, Component, Component) -> boolean) + & (World, Entity, Component, Component, Component, Component) -> boolean, --- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil. parent: (self: World, entity: Entity) -> Entity?, @@ -217,33 +310,33 @@ export type World = { --- Searches the world for entities that match a given query query: ((World) -> Query) - & ((World, Id) -> Query) - & ((World, Id, Id) -> Query) - & ((World, Id, Id, Id) -> Query) - & ((World, Id, Id, Id, Id) -> Query) - & ((World, Id, Id, Id, Id, Id) -> Query) - & ((World, Id, Id, Id, Id, Id, Id) -> Query) + & ((World, Component) -> Query) + & ((World, Component, Component) -> Query) + & ((World, Component, Component, Component) -> Query) + & ((World, Component, Component, Component, Component) -> Query) + & ((World, Component, Component, Component, Component, Component) -> Query) + & ((World, Component, Component, Component, Component, Component, Component) -> Query) & (( World, - Id, - Id, - Id, - Id, - Id, - Id, - Id + Component, + Component, + Component, + Component, + Component, + Component, + Component ) -> Query) & (( World, - Id, - Id, - Id, - Id, - Id, - Id, - Id, - Id, - ...Id + Component, + Component, + Component, + Component, + Component, + Component, + Component, + Component, + ...Component ) -> Query), } @@ -262,7 +355,7 @@ export type ComponentRecord = { on_change: ((entity: Entity, id: Entity, value: T, oldArchetype: Archetype) -> ())?, on_remove: ((entity: Entity, id: Entity) -> ())?, } -export type ComponentIndex = Map +export type ComponentIndex = Map export type Archetypes = { [i24]: Archetype } export type EntityIndex = { @@ -1250,7 +1343,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any) local columns_map = archetype.columns_map local ids = query.ids - local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id }) + local A, B, C, D, E, F, G, H, I = unpack(ids :: { Component }) local a: Column, b: Column, c: Column, d: Column local e: Column, f: Column, g: Column, h: Column @@ -1606,7 +1699,7 @@ local function query_cached(query: QueryInner) local ids = query.ids local lastArchetype = 1 - local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id }) + local A, B, C, D, E, F, G, H, I = unpack(ids :: { Component }) if not A then A = query.filter_with[1] end @@ -1617,8 +1710,15 @@ local function query_cached(query: QueryInner) local entities: { Entity } local i: number local archetype: Archetype - local columns_map: { [Id]: Column } - local archetypes = query_archetypes(query :: any) + local columns_map: { [Component]: Column } + local archetypes = query_archetypes(query :: any) :: { Archetype } + local archetypes_map = {} + query.archetypes_map = archetypes_map + + for j, arche in archetypes do + archetypes_map[arche.id] = j + end + local compatible_archetypes = archetypes :: { Archetype } local world = query.world @@ -1627,10 +1727,10 @@ local function query_cached(query: QueryInner) local observable = world.observable local on_create_action = observable[EcsOnArchetypeCreate::any] if not on_create_action then - on_create_action = {} :: Map + on_create_action = {} :: Map observable[EcsOnArchetypeCreate::any] = on_create_action end - local query_cache_on_create = on_create_action[A] + local query_cache_on_create: { Observer } = on_create_action[A] if not query_cache_on_create then query_cache_on_create = {} on_create_action[A] = query_cache_on_create @@ -1638,31 +1738,36 @@ local function query_cached(query: QueryInner) local on_delete_action = observable[EcsOnArchetypeDelete::any] if not on_delete_action then - on_delete_action = {} :: Map + on_delete_action = {} :: Map observable[EcsOnArchetypeDelete::any] = on_delete_action end - local query_cache_on_delete = on_delete_action[A] + local query_cache_on_delete: { Observer } = on_delete_action[A] if not query_cache_on_delete then query_cache_on_delete = {} on_delete_action[A] = query_cache_on_delete end - local function on_create_callback(archetype) - table.insert(archetypes, archetype) + local function on_create_callback(archetype: Archetype) + local n = #archetypes + 1 + archetypes[n] = archetype + archetypes_map[archetype.id] = n end local function on_delete_callback(archetype) - local i = table.find(archetypes, archetype) :: number - if i == nil then - return - end local n = #archetypes - archetypes[i] = archetypes[n] + local lastarchetype = archetypes[n] + local archetypeid = archetype.id + local i = archetypes_map[archetypeid] + + archetypes[i] = lastarchetype archetypes[n] = nil + + archetypes_map[archetypeid] = nil + archetypes_map[lastarchetype.id] = i end - local observer_for_create = { query = query, callback = on_create_callback } - local observer_for_delete = { query = query, callback = on_delete_callback } + local observer_for_create = { query = query, callback = on_create_callback } :: Observer + local observer_for_delete = { query = query, callback = on_delete_callback } :: Observer table.insert(query_cache_on_create, observer_for_create) table.insert(query_cache_on_delete, observer_for_delete) @@ -2014,14 +2119,61 @@ local function query_cached(query: QueryInner) end end + local eindex = world.entity_index :: entityindex + + local function cached_query_has(entity): boolean + local r = entity_index_try_get_fast(eindex, entity) + if not r then + return false + end + + local entityarchetype = r.archetype + if not entityarchetype then + return false + end + + return archetypes_map[entityarchetype.id] ~= nil + end + local cached_query = query :: any cached_query.archetypes = query_archetypes cached_query.__iter = cached_query_iter cached_query.iter = cached_query_iter + cached_query.has = cached_query_has setmetatable(cached_query, cached_query) return cached_query end +local function query_has(query: QueryInner, entity: i53) + local world = query.world :: world + local r = entity_index_try_get(world.entity_index, entity) + if not r then + return false + end + local archetype = r.archetype + if not archetype then + return false + end + + local columns_map = archetype.columns_map + for _, component in query.filter_with :: {number} do + if not columns_map[component] then + return false + end + end + + local filter_without = query.filter_without + if filter_without then + for _, component in filter_without :: {number} do + if columns_map[component] then + return false + end + end + end + + return true +end + local Query = {} Query.__index = Query Query.__iter = query_iter @@ -2030,6 +2182,7 @@ Query.without = query_without Query.with = query_with Query.archetypes = query_archetypes Query.cached = query_cached +Query.has = query_has local function world_query(world: World, ...) local ids = { ... } @@ -2217,7 +2370,9 @@ local function world_new() max_id = 0, } :: entityindex - local component_index = {} :: Map + -- NOTE(marcus): with the way the component index is accessed, we want to + -- ensure that components range has fast access. + local component_index = table.create(EcsRest) :: Map local archetype_index = {} :: { [string]: archetype } local archetypes = {} :: Map @@ -2749,7 +2904,7 @@ local function world_new() world_set(world, component, EcsOnRemove, on_remove) end - table.insert(listeners, fn) + table.insert(listeners, fn::Listener) return function() local n = #listeners @@ -3071,9 +3226,9 @@ local function world_new() local entities = idr_r_archetype.entities local tr = records[archetype_id] local tr_count = counts[archetype_id] - local types = idr_r_archetype.types + local idr_r_types = idr_r_archetype.types for i = tr, tr + tr_count - 1 do - local id = types[i] + local id = idr_r_types[i] node = archetype_traverse_remove(world, id, node) local on_remove = component_index[id].on_remove if on_remove then @@ -3247,23 +3402,6 @@ local function world_new() return world end --- type function ecs_id_t(ty) --- local __T = ty:readproperty(types.singleton("__T")) --- if not __T then --- return ty:readproperty(types.singleton("__jecs_pair_value")) --- end --- return __T --- end - --- type function ecs_pair_t(first, second) --- if ecs_id_t(first):is("nil") then --- return second --- else --- return first --- end --- end --- - local function ecs_is_tag(world: world, entity: i53): boolean local idr = world.component_index[entity] if idr then @@ -3282,51 +3420,51 @@ return { new = world_new }, component = (ECS_COMPONENT :: any) :: () -> Entity, - tag = (ECS_TAG :: any) :: () -> Entity, - meta = (ECS_META :: any) :: (id: Entity, id: Id, value: a?) -> Entity, - is_tag = (ecs_is_tag :: any) :: (World, Id) -> boolean, + tag = (ECS_TAG :: any) :: () -> Entity, + meta = (ECS_META :: any) :: (id: Entity, id: Component, value: a?) -> Entity, + is_tag = (ecs_is_tag :: any) :: (World, Component) -> boolean, - OnAdd = (EcsOnAdd :: any) :: Id<(entity: Entity, id: Id, data: T) -> ()>, - OnRemove = (EcsOnRemove :: any) :: Id<(entity: Entity, id: Id) -> ()>, - OnChange = (EcsOnChange :: any) :: Id<(entity: Entity, id: Id, data: T) -> ()>, - ChildOf = (EcsChildOf :: any) :: Entity, - Component = (EcsComponent :: any) :: Entity, - Wildcard = (EcsWildcard :: any) :: Id, - w = (EcsWildcard :: any) :: Id, - OnDelete = (EcsOnDelete :: any) :: Entity, - OnDeleteTarget = (EcsOnDeleteTarget :: any) :: Entity, - Delete = (EcsDelete :: any) :: Entity, - Remove = (EcsRemove :: any) :: Entity, - Name = (EcsName :: any) :: Id, - Exclusive = (EcsExclusive :: any) :: Entity, - ArchetypeCreate = (EcsOnArchetypeCreate :: any) :: Entity, - ArchetypeDelete = (EcsOnArchetypeDelete :: any) :: Entity, - Rest = (EcsRest :: any) :: Entity, + OnAdd = (EcsOnAdd :: any) :: Component<(entity: Entity, id: Id, data: T) -> ()>, + OnRemove = (EcsOnRemove :: any) :: Component<(entity: Entity, id: Id) -> ()>, + OnChange = (EcsOnChange :: any) :: Component<(entity: Entity, id: Id, data: T) -> ()>, + ChildOf = (EcsChildOf :: any) :: Entity, + Component = (EcsComponent :: any) :: Entity, + Wildcard = (EcsWildcard :: any) :: Component, + w = (EcsWildcard :: any) :: Component, + OnDelete = (EcsOnDelete :: any) :: Entity, + OnDeleteTarget = (EcsOnDeleteTarget :: any) :: Entity, + Delete = (EcsDelete :: any) :: Entity, + Remove = (EcsRemove :: any) :: Entity, + Name = (EcsName :: any) :: Component, + Exclusive = (EcsExclusive :: any) :: Entity, + ArchetypeCreate = (EcsOnArchetypeCreate :: any) :: Entity, + ArchetypeDelete = (EcsOnArchetypeDelete :: any) :: Entity, + Rest = (EcsRest :: any) :: Entity, - pair = ECS_PAIR :: (first: Id

, second: Id) -> Pair, + pair = ECS_PAIR :: (first: Entity

, second: Entity) -> Pair, - IS_PAIR = ECS_IS_PAIR :: (pair: Pair) -> boolean, - ECS_PAIR_FIRST = ECS_PAIR_FIRST :: (pair: Pair) -> Id

, - ECS_PAIR_SECOND = ECS_PAIR_SECOND :: (pair: Pair) -> Id, - pair_first = ecs_pair_first :: (world: World, pair: Pair) -> Id

, - pair_second = ecs_pair_second :: (world: World, pair: Pair) -> Id, + IS_PAIR = ECS_IS_PAIR :: (pair: Component) -> boolean, + ECS_PAIR_FIRST = ECS_PAIR_FIRST :: (pair: Id) -> Component

, + ECS_PAIR_SECOND = ECS_PAIR_SECOND :: (pair: Id) -> Component, + pair_first = ecs_pair_first :: (world: World, pair: Id) -> Component

, + pair_second = ecs_pair_second :: (world: World, pair: Id) -> Component, entity_index_get_alive = entity_index_get_alive, archetype_append_to_records = archetype_append_to_records, - id_record_ensure = id_record_ensure :: (World, Id) -> ComponentRecord, - component_record = id_record_get :: (World, Id) -> ComponentRecord?, + id_record_ensure = id_record_ensure :: (World, Component) -> ComponentRecord, + component_record = id_record_get :: (World, Component) -> ComponentRecord?, record = ecs_entity_record :: (World, Entity) -> Record, - archetype_create = archetype_create :: (World, { Id }, string) -> Archetype, - archetype_ensure = archetype_ensure :: (World, { Id }) -> Archetype, + archetype_create = archetype_create :: (World, { Component }, string) -> Archetype, + archetype_ensure = archetype_ensure :: (World, { Component }) -> Archetype, find_insert = find_insert, - find_archetype_with = find_archetype_with :: (World, Id, Archetype) -> Archetype, - find_archetype_without = find_archetype_without :: (World, Id, Archetype) -> Archetype, + find_archetype_with = find_archetype_with :: (World, Component, Archetype) -> Archetype, + find_archetype_without = find_archetype_without :: (World, Component, Archetype) -> Archetype, create_edge_for_remove = create_edge_for_remove, - archetype_traverse_add = archetype_traverse_add :: (World, Id, Archetype) -> Archetype, - archetype_traverse_remove = archetype_traverse_remove :: (World, Id, Archetype) -> Archetype, - bulk_insert = ecs_bulk_insert :: (World, Entity, { Id }, { any }) -> (), - bulk_remove = ecs_bulk_remove :: (World, Entity, { Id }) -> (), + archetype_traverse_add = archetype_traverse_add :: (World, Component, Archetype) -> Archetype, + archetype_traverse_remove = archetype_traverse_remove :: (World, Component, Archetype) -> Archetype, + bulk_insert = ecs_bulk_insert :: (World, Entity, { Component }, { any }) -> (), + bulk_remove = ecs_bulk_remove :: (World, Entity, { Component }) -> (), entity_move = entity_move :: (EntityIndex, Entity, Record, Archetype) -> (), @@ -3345,7 +3483,7 @@ return { query_archetypes = query_archetypes, query_match = query_match, - find_observers = find_observers :: (World, Id, Id) -> { Observer }, + find_observers = find_observers :: (World, Component, Component) -> { Observer }, -- Inwards facing API for testing ECS_ID = ECS_ENTITY_T_LO :: (Entity) -> number, diff --git a/test/addons/ob.luau b/test/addons/ob.luau index 9bed301..3de26e2 100755 --- a/test/addons/ob.luau +++ b/test/addons/ob.luau @@ -24,6 +24,7 @@ TEST("addons/ob::observer", function() world:add(e, jecs.pair(A, B)) CHECK(c==2) world:add(e, jecs.pair(A, C)) + print(c) CHECK(c==2) end