From 846722ba58b82ee894004e0e0a2f53f3ebdd4930 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Fri, 25 Apr 2025 15:47:51 +0200 Subject: [PATCH] Initialize bundle after builtn components are set --- addons/observers.luau | 145 +++++++++++++++++++++++++++++++++++++++--- jecs.luau | 20 +++--- 2 files changed, 147 insertions(+), 18 deletions(-) diff --git a/addons/observers.luau b/addons/observers.luau index a7a8191..f52ed45 100644 --- a/addons/observers.luau +++ b/addons/observers.luau @@ -24,7 +24,7 @@ local function observers_new(world, description) end local entity_index = world.entity_index :: any - local function emplaced(entity: jecs.Entity) + local function emplaced(entity, id, value) local r = jecs.entity_index_try_get_fast( entity_index, entity :: any) @@ -45,6 +45,112 @@ local function observers_new(world, description) end end + +local function join(world, component) + local sparse_array = {} + local dense_array = {} + local values = {} + local max_id = 0 + + world:added(component, function(entity, id, value) + max_id += 1 + sparse_array[entity] = max_id + dense_array[max_id] = entity + values[max_id] = value + end) + + world:removed(component, function(entity, id) + local e_swap = dense_array[max_id] + local v_swap = values[max_id] + + local dense = sparse_array[entity] + dense_array[dense] = e_swap + values[dense] = v_swap + + sparse_array[entity] = nil + dense_array[max_id] = nil + values[max_id] = nil + end) + + world:changed(component, function(entity, id, value) + values[sparse_array[entity]] = value + end) + + return function() + local i = max_id + return function(): ...any + i -= 1 + if i == 0 then + return nil + end + local e = dense_array[i] + return e, values[i] + end + end +end + +local positions_join = join(world, Position) + +for e, v in positions_join() do + +end + +local function query_changed(world, component) + assert(jecs.IS_PAIR(component) == false) + local callerid = debug.info(2, "sl") + + local tracker = world.trackers[callerid] + if not tracker then + local records = {} + local connections = {} + tracker = { + records = records, + connections = connections + } + world.trackers[callerid] = tracker + + table.insert(connections, world:added(component, function(entity, id, v) + tracker[entity] = { + new = v + } + end)) + table.insert(connections, world:changed(component, function(entity, id, v) + local record = tracker[entity] + record.old = record.new + record.new = v + end)) + + table.insert(connections, world:removed(component, function(entity, id) + local record = tracker[entity] + record.old = record.new + record.new = nil + end)) + end + + local entity = nil + local record = nil + return function() + entity, record = next(tracker, entity) + if entity == nil then + return + end + return entity, record + end +end + +local function spy_on_world_delete(world) + local world_delete = world.delete + world.delete = function(world, entity) + world_delete(world, entity) + for _, tracker in world.trackers do + tracker.records[entity] = nil + for _, connection in tracker.connections do + connection() + end + end + end +end + local function monitors_new(world, description) local query = description.query local callback = description.callback @@ -93,14 +199,16 @@ local function monitors_new(world, description) end local function observers_add(world: jecs.World & { [string]: any }): PatchedWorld + type Signal = { [jecs.Entity]: { (...any) -> () } } local signals = { - added = {}, - emplaced = {}, - removed = {} + added = {} :: Signal, + emplaced = {} :: Signal, + removed = {} :: Signal } world.added = function(_, component, fn) local listeners = signals.added[component] + local max = #listeners + 1 local component_index = world.component_index :: jecs.ComponentIndex assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with") if not listeners then @@ -111,16 +219,24 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl listener(entity, id, value) end end - world:set(component, jecs.OnAdd, on_add) end - table.insert(listeners, fn) + world:set(component, jecs.OnAdd, on_add) + end + listeners[max] = fn + return function() + local n = #listeners + listeners[max] = listeners[n] + listeners[n] = nil + end end world.changed = function(_, component, fn) local listeners = signals.emplaced[component] + local max = 0 local component_index = world.component_index :: jecs.ComponentIndex assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with") if not listeners then listeners = {} + max = 1 signals.emplaced[component] = listeners local function on_change(entity: number, id: number, value: any) for _, listener in listeners :: any do @@ -128,12 +244,18 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl end end world:set(component, jecs.OnChange, on_change) + else + max = #listeners + 1 + end + listeners[max] = fn + return function() + end - table.insert(listeners, fn) end world.removed = function(_, component, fn) local listeners = signals.removed[component] + local max = #listeners local component_index = world.component_index :: jecs.ComponentIndex assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with") if not listeners then @@ -146,7 +268,12 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl end world:set(component, jecs.OnRemove, on_remove) end - table.insert(listeners, fn) + listeners[max] = fn + return function() + local n = #listeners + listeners[max] = listeners[n] + listeners[n] = nil + end end world.signals = signals @@ -155,6 +282,8 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl world.monitor = monitors_new + world.trackers = {} + return world :: PatchedWorld end diff --git a/jecs.luau b/jecs.luau index 3f297da..b9cc92a 100644 --- a/jecs.luau +++ b/jecs.luau @@ -2372,16 +2372,6 @@ local function world_new() entity_index_new_id(entity_index) end - for i, bundle in ecs_metadata do - for ty, value in bundle do - if value == NULL then - world_add(self, i, ty) - else - world_set(self, i, ty, value) - end - end - end - world_add(self, EcsName, EcsComponent) world_add(self, EcsOnChange, EcsComponent) world_add(self, EcsOnAdd, EcsComponent) @@ -2404,6 +2394,16 @@ local function world_new() world_add(self, EcsChildOf, ECS_PAIR(EcsOnDeleteTarget, EcsDelete)) + for i, bundle in ecs_metadata do + for ty, value in bundle do + if value == NULL then + world_add(self, i, ty) + else + world_set(self, i, ty, value) + end + end + end + return self end