From ba74d6b471fe757314e7056d3e7469007e11a485 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Tue, 24 Dec 2024 07:38:38 +0100 Subject: [PATCH] Initial commit --- jecs.luau | 168 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 67 deletions(-) diff --git a/jecs.luau b/jecs.luau index ff93b00..a94568b 100644 --- a/jecs.luau +++ b/jecs.luau @@ -89,7 +89,8 @@ local EcsOnDeleteTarget = HI_COMPONENT_ID + 8 local EcsDelete = HI_COMPONENT_ID + 9 local EcsRemove = HI_COMPONENT_ID + 10 local EcsName = HI_COMPONENT_ID + 11 -local EcsRest = HI_COMPONENT_ID + 12 +local EcsTableCreate = HI_COMPONENT_ID + 12 +local EcsRest = HI_COMPONENT_ID + 13 local ECS_PAIR_FLAG = 0x8 local ECS_ID_FLAGS_MASK = 0x10 @@ -250,6 +251,40 @@ local function ecs_pair_second(world, e) return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e)) end +local function query_match(query, archetype) + local matches = true + + local records = archetype.records + for _, id in query.ids do + if not records[id] then + matches = false + break + end + end + + return matches +end + +local function observer_invoke(observer, event) + table.insert(observer.query.compatible_archetypes, event.archetype) +end + +local function emit(world: World, event) + local map = world.observerable[event.id] + if not map then + return + end + local observer_list: {[string]: any} = map[event.component] + if not observer_list then + return + end + for _, observer in observer_list do + if query_match(observer.query, event.archetype) then + observer_invoke(observer, event) + end + end +end + local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: i24, from: Archetype, src_row: i24) local src_columns = from.columns local dst_columns = to.columns @@ -535,15 +570,49 @@ local function archetype_append_to_records( end end -local function archetype_create(world: World, types: { i24 }, ty, prev: i53?): Archetype +local function create_observer_uni(world: World, component: number, event) + local map = world.observerable[event] + if not map then + map = {} + world.observerable[event] = map + end + + local observer_list = map[component] + if not observer_list then + observer_list = {} + map[component] = observer_list + end + + local observer = {} + + table.insert(observer_list, observer) + + return observer +end + +local function archetype_create(world: World, id_types: { i24 }, ty, prev: i53?): Archetype local archetype_id = (world.nextArchetypeId :: number) + 1 world.nextArchetypeId = archetype_id - local length = #types + local length = #id_types local columns = (table.create(length) :: any) :: { Column } local records: { ArchetypeRecord } = {} - for i, componentId in types do + + local archetype: Archetype = { + columns = columns, + entities = {}, + id = archetype_id, + records = records, + type = ty, + types = id_types, + + add = {}, + remove = {}, + refs = {} :: GraphEdge, + } + + for i, componentId in id_types do local idr = id_record_ensure(world, componentId) archetype_append_to_records(idr, archetype_id, records, componentId, i) @@ -564,21 +633,10 @@ local function archetype_create(world: World, types: { i24 }, ty, prev: i53?): A else columns[i] = NULL_ARRAY end + + emit(world, { id = EcsTableCreate, component = componentId, archetype = archetype}) end - local archetype: Archetype = { - columns = columns, - entities = {}, - id = archetype_id, - records = records, - type = ty, - types = types, - - add = {}, - remove = {}, - refs = {} :: GraphEdge, - } - world.archetypeIndex[ty] = archetype world.archetypes[archetype_id] = archetype @@ -1477,6 +1535,15 @@ local function query_archetypes(query) return query.compatible_archetypes end +local function query_cached(query) + for _, component in query.ids do + local observer = create_observer_uni(query.world, component, EcsTableCreate) + observer.query = query + end + + return query +end + local Query = {} Query.__index = Query Query.__iter = query_iter @@ -1484,6 +1551,7 @@ Query.iter = query_iter_init Query.without = query_without Query.with = query_with Query.archetypes = query_archetypes +Query.cached = query_cached local function world_query(world: World, ...) local compatible_archetypes = {} @@ -1496,10 +1564,16 @@ local function world_query(world: World, ...) local idr: IdRecord? local componentIndex = world.componentIndex + local q = setmetatable({ + ids = ids, + compatible_archetypes = compatible_archetypes, + world = world, + }, Query) + for _, id in ids do local map = componentIndex[id] if not map then - return EMPTY_QUERY + return q end if idr == nil or map.size < idr.size then @@ -1508,7 +1582,7 @@ local function world_query(world: World, ...) end if not idr then - return EMPTY_QUERY + return q end for archetype_id in idr.cache do @@ -1536,15 +1610,6 @@ local function world_query(world: World, ...) compatible_archetypes[length] = compatibleArchetype end - if length == 0 then - return EMPTY_QUERY - end - - local q = setmetatable({ - compatible_archetypes = compatible_archetypes, - ids = ids, - }, Query) :: any - return q end @@ -1730,6 +1795,7 @@ function World.new() nextComponentId = 0 :: number, nextEntityId = 0 :: number, ROOT_ARCHETYPE = (nil :: any) :: Archetype, + observerable = {} }, World) :: any self.ROOT_ARCHETYPE = archetype_create(self, {}, "") @@ -1793,13 +1859,14 @@ export type Entity = number & { __T: T } type Iter = (query: Query) -> () -> (Entity, T...) -type Query = typeof(setmetatable({}, { +export type Query = typeof(setmetatable({}, { __iter = (nil :: any) :: Iter, })) & { iter: Iter, - with: (self: Query, ...i53) -> Query, - without: (self: Query, ...i53) -> Query, + with: (self: Query, ...Id) -> Query, + without: (self: Query, ...Id) -> Query, archetypes: (self: Query) -> { Archetype }, + cached: (self: Query) -> Query } export type World = { @@ -1812,6 +1879,8 @@ export type World = { nextComponentId: number, nextEntityId: number, nextArchetypeId: number, + + observerable: { [string]: { [Id]: { query: Query } } } } & { --- Creates a new entity entity: (self: World) -> Entity, @@ -1854,42 +1923,7 @@ export type World = { children: (self: World, id: Id) -> () -> Entity, --- Searches the world for entities that match a given query - query: ((self: World, Id) -> Query) - & ((self: World, Id, Id) -> Query) - & ((self: World, Id, Id, Id) -> Query) - & ((self: World, Id, Id, Id, Id) -> Query) - & ((self: World, Id, Id, Id, Id, Id) -> Query) - & (( - self: World, - Id, - Id, - Id, - Id, - Id, - Id - ) -> Query) - & (( - self: World, - Id, - Id, - Id, - Id, - Id, - Id, - Id - ) -> Query) - & (( - self: World, - Id, - Id, - Id, - Id, - Id, - Id, - Id, - Id, - ...Id - ) -> Query), + query: ((self: World, a: { __T: A }) -> Query) } return {