Compare commits

...

4 commits

Author SHA1 Message Date
Ukendio
edb73ca49a Use generalized iteration
Some checks failed
Analysis / Run Luau Analyze (push) Has been cancelled
Unit Testing / Run Luau Tests (push) Has been cancelled
2024-12-26 06:12:23 +01:00
Ukendio
76aef4f1b1 Fix metatable 2024-12-26 06:11:43 +01:00
Ukendio
97fe91387f Rework demo 2024-12-26 06:03:50 +01:00
Ukendio
2e29046770 Inverse statement 2024-12-26 05:35:59 +01:00
18 changed files with 303 additions and 462 deletions

View file

@ -2,18 +2,16 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService") local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService") local UserInputService = game:GetService("UserInputService")
local jabby = require(ReplicatedStorage.Packages.jabby) local jabby = require(ReplicatedStorage.Packages.jabby)
local std = require(ReplicatedStorage.std) local std = ReplicatedStorage.std
local Scheduler = std.Scheduler local scheduler = require(std.scheduler)
local world = std.world local world = require(std.world)
local function start(modules) local function start(modules)
local scheduler = Scheduler.new(world, require(ReplicatedStorage.std.components))
for _, module in modules do for _, module in modules do
require(module)(scheduler) require(module)
end end
local events = scheduler.collect.all() local events = scheduler.COLLECT()
scheduler.systems.begin(events) scheduler.BEGIN(events)
jabby.set_check_function(function(player) jabby.set_check_function(function(player)
return true return true
end) end)

View file

@ -3,15 +3,15 @@
-- original author @centau -- original author @centau
local SUCCESS = 0 local FAILURE = -1
local FAILURE = 1 local RUNNING = 0
local RUNNING = 2 local SUCCESS = 1
local function SEQUENCE(nodes) local function SEQUENCE(nodes)
return function(...) return function(...)
for _, node in nodes do for _, node in nodes do
local status = node(...) local status = node(...)
if status == FAILURE or status == RUNNING then if status <= RUNNING then
return status return status
end end
end end
@ -23,7 +23,7 @@ local function FALLBACK(nodes)
return function(...) return function(...)
for _, node in nodes do for _, node in nodes do
local status = node(...) local status = node(...)
if status == SUCCESS or status == RUNNING then if status > FAILURE then
return status return status
end end
end end

View file

@ -8,7 +8,7 @@ local components: {
Model: Entity<Model>, Model: Entity<Model>,
Player: Entity, Player: Entity,
Target: Entity, Target: Entity,
Transform: Entity<CFrame>, Transform: Entity<{ new: CFrame, old: CFrame }>,
Velocity: Entity<number>, Velocity: Entity<number>,
Previous: Entity, Previous: Entity,
} = } =
@ -23,4 +23,8 @@ local components: {
Previous = world:component(), Previous = world:component(),
} }
for name, component in components :: {[string]: jecs.Entity} do
world:set(component, jecs.Name, name)
end
return table.freeze(components) return table.freeze(components)

View file

@ -1,11 +0,0 @@
local handle = require(script.Parent.handle)
local world = require(script.Parent.world)
local singleton = world:entity()
local function ctx()
-- Cannot cache handles because they will get invalidated
return handle(singleton)
end
return ctx

View file

@ -1,56 +0,0 @@
local jecs = require(game:GetService("ReplicatedStorage").ecs)
local world = require(script.Parent.world)
type Handle = {
has: (self: Handle, id: jecs.Entity) -> boolean,
get: <T>(self: Handle, id: jecs.Entity<T>) -> T?,
add: <T>(self: Handle, id: jecs.Entity<T>) -> Handle,
set: <T>(self: Handle, id: jecs.Entity<T>, value: T) -> Handle,
id: (self: Handle?) -> jecs.Entity,
}
local handle: (e: jecs.Entity) -> Handle
do
local e
local function has(_, id)
return world:has(e, id)
end
local function get(_, id)
return world:get(e, id)
end
local function set(self, id, value)
world:set(e, id, value)
return self
end
local function add(self, id)
world:add(e, id)
return self
end
local function clear(self)
world:clear(e)
return self
end
local function delete(self)
world:delete(e)
end
local function id()
return e
end
local entity = {
has = has,
get = get,
set = set,
add = add,
clear = clear,
id = id,
}
function handle(id)
e = id
return entity
end
end
return handle

View file

@ -1,32 +0,0 @@
--!native
--!optimize 2
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local jecs = require(ReplicatedStorage.ecs)
local function create_cache(hook)
local columns = setmetatable({}, {
__index = function(self, component)
local column = {}
self[component] = column
return column
end,
})
return function(world, component, fn)
local column = columns[component]
table.insert(column, fn)
world:set(component, hook, function(entity, value)
for _, callback in column do
callback(entity, value)
end
end)
end
end
local hooks = {
OnSet = create_cache(jecs.OnSet),
OnAdd = create_cache(jecs.OnAdd),
OnRemove = create_cache(jecs.OnRemove),
}
return hooks

View file

@ -1,25 +0,0 @@
local jecs = require(game:GetService("ReplicatedStorage").ecs)
local world = require(script.world) :: jecs.World
export type World = jecs.World
local Scheduler = require(script.scheduler)
export type Scheduler = Scheduler.Scheduler
local std = {
ChangeTracker = require(script.changetracker),
Scheduler = Scheduler,
bt = require(script.bt),
collect = require(script.collect),
components = require(script.components),
ctx = require(script.ctx),
handle = require(script.handle),
interval = require(script.interval),
ref = require(script.ref),
world = world :: World,
pair = jecs.pair,
__ = jecs.w,
hooks = require(script.hooks),
}
return std

View file

@ -0,0 +1,14 @@
local std = game:GetService("ReplicatedStorage").std
local Players = game:GetService("Players")
local scheduler = require(std.scheduler)
local PHASE = scheduler.PHASE
return {
PlayerAdded = PHASE({
event = Players.PlayerAdded
}),
PlayerRemoved = PHASE({
event = Players.PlayerRemoving
})
}

View file

@ -1,16 +1,18 @@
local handle = require(script.Parent.handle)
local world = require(script.Parent.world) local world = require(script.Parent.world)
local refs = {} local jecs = require(game:GetService("ReplicatedStorage").ecs)
local refs: {[any]: jecs.Entity} = {}
local function fini(key) local function fini(key): () -> ()
return function() return function()
refs[key] = nil refs[key] = nil
end end
end end
local function ref(key): (handle.Handle, (() -> ())?) local function noop() end
local function ref(key): (jecs.Entity, () -> ())
if not key then if not key then
return handle(world:entity()) return world:entity(), noop
end end
local e = refs[key] local e = refs[key]
if not e then if not e then
@ -18,7 +20,7 @@ local function ref(key): (handle.Handle, (() -> ())?)
refs[key] = e refs[key] = e
end end
-- Cannot cache handles because they will get invalidated -- Cannot cache handles because they will get invalidated
return handle(e), fini(key) return e, fini(key)
end end
return ref return ref

View file

@ -1,31 +0,0 @@
local reserved = 0
local function reserve()
reserved += 1
return reserved
end
-- If you don't like passing around a world singleton
-- and you need to register component IDs, just register them.
-- I dont use this because I like adding component traits
--[[
local components = {
Model = registry.reserve(),
Transform = registry.reserve(),
}
local world = registry.register(jecs.World.new())
local e = world:entity()
world:set(e, components.Transform, CFrame)
]]
local function register(world)
for _ = 1, reserved do
world:component()
end
return world
end
return {
reserve = reserve,
register = register,
}

View file

@ -1,6 +1,7 @@
--!native --!native
--!optimize 2 --!optimize 2
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local jabby = require(ReplicatedStorage.Packages.jabby) local jabby = require(ReplicatedStorage.Packages.jabby)
local jecs = require(ReplicatedStorage.ecs) local jecs = require(ReplicatedStorage.ecs)
local pair = jecs.pair local pair = jecs.pair
@ -8,6 +9,7 @@ local Name = jecs.Name
type World = jecs.World type World = jecs.World
type Entity<T = nil> = jecs.Entity<T> type Entity<T = nil> = jecs.Entity<T>
type Id<T = unknown> = jecs.Id<T>
type System = { type System = {
callback: (world: World) -> (), callback: (world: World) -> (),
@ -21,115 +23,92 @@ type Events = {
Heartbeat: Systems, Heartbeat: Systems,
} }
export type Scheduler = { local world = require(script.Parent.world)
components: { local Disabled = world:entity()
Disabled: Entity, local System = world:component() :: Id<{ callback: (any) -> (), name: string}>
System: Entity<System>, local DependsOn = world:entity()
Phase: Entity, local Event = world:component() :: Id<RBXScriptSignal>
DependsOn: Entity, local Phase = world:entity()
},
collect: { local PreRender = world:entity()
under_event: (event: Entity) -> Systems, local Heartbeat = world:entity()
all: () -> Events, local PreAnimation = world:entity()
}, local PreSimulation = world:entity()
systems: {
begin: (events: Events) -> { [Entity]: thread },
new: (callback: (dt: number) -> (), phase: Entity) -> Entity,
},
phases: {
RenderStepped: Entity,
Heartbeat: Entity,
},
phase: (after: Entity) -> Entity,
debugging: boolean,
}
local scheduler_new: (w: World, components: { [string]: Entity }) -> Scheduler
do
local world: World
local Disabled: Entity
local System: Entity<System>
local DependsOn: Entity
local Phase: Entity
local Event: Entity<RBXScriptSignal>
local scheduler
local RenderStepped
local Heartbeat
local PreAnimation
local PreSimulation
local sys: System local sys: System
local dt local dt: number
local jabby_scheduler = jabby.scheduler.create("Scheduler")
local a, b, c, d
local function run() local function run()
local id = sys.id local id = sys.id
scheduler:run(id, sys.callback, dt) jabby_scheduler:run(id, sys.callback, a, b, c, d)
return nil
end end
local function panic(str) world:add(Heartbeat, Phase)
-- We don't want to interrupt the loop when we error world:set(Heartbeat, Event, RunService.Heartbeat)
task.spawn(error, str)
world:add(PreSimulation, Phase)
world:set(PreSimulation, Event, RunService.PreSimulation)
world:add(PreAnimation, Phase)
world:set(PreAnimation, Event, RunService.PreAnimation)
table.insert(jabby.public, {
class_name = "World",
name = "MyWorld",
world = world,
debug = Name,
entities = {},
})
jabby.public.updated = true
table.insert(jabby.public, jabby_scheduler)
if RunService:IsClient() then
world:add(PreRender, Phase)
world:set(PreRender, Event, (RunService :: RunService).PreRender)
end end
local function begin(events: { Systems })
local function begin(events: { [RBXScriptSignal]: Systems })
local connections = {} local connections = {}
for event, systems in events do for event, systems in events do
if not event then if not event then
continue continue
end end
local event_name = tostring(event) local event_name = tostring(event)
connections[event] = event:Connect(function(delta) connections[event] = event:Connect(function(...)
debug.profilebegin(event_name) debug.profilebegin(event_name)
for _, s in systems do for _, s in systems do
sys = s sys = s
dt = delta a, b, c, d = ...
local didNotYield, why = xpcall(function()
for _ in run do for _ in run do
break break
end end
end, debug.traceback)
if didNotYield then
continue
end
if string.find(why, "thread is not yieldable") then
panic(
"Not allowed to yield in the systems."
.. "\n"
.. "System: "
.. debug.info(s.callback, "n")
.. " has been ejected"
)
continue
end
panic(why)
end end
debug.profileend() debug.profileend()
end) end)
end end
return threads return connections
end end
local function scheduler_collect_systems_under_phase_recursive(systems, phase) local function scheduler_collect_systems_under_phase_recursive(systems, phase: Entity)
local phase_name = world:get(phase, Name) local phase_name = world:get(phase, Name)
for _, s in world:query(System):with(pair(DependsOn, phase)) do for _, s in world:query(System):with(pair(DependsOn, phase)) do
table.insert(systems, { table.insert(systems, {
id = scheduler:register_system({ id = jabby_scheduler:register_system({
name = s.name, name = s.name,
phase = phase_name, phase = phase_name,
}), } :: any),
callback = s.callback, callback = s.callback,
}) })
end end
for after in world:query(Phase):with(pair(DependsOn, phase)) do for after in world:query(Phase):with(pair(DependsOn, phase)):iter() do
scheduler_collect_systems_under_phase_recursive(systems, after) scheduler_collect_systems_under_phase_recursive(systems, after)
end end
end end
@ -148,99 +127,41 @@ do
return events return events
end end
local function scheduler_phase_new(after) local function scheduler_phase_new(d: { after: Entity?, event: RBXScriptSignal? })
local phase = world:entity() local phase = world:entity()
world:add(phase, Phase) world:add(phase, Phase)
local dependency = pair(DependsOn, after) local after = d.after
if after then
local dependency = pair(DependsOn, after :: Entity)
world:add(phase, dependency) world:add(phase, dependency)
end
local event = d.event
if event then
world:set(phase, Event, event)
end
return phase return phase
end end
local function scheduler_systems_new(callback, phase) local function scheduler_systems_new(callback: (any) -> (), phase: Entity?)
local system = world:entity() local system = world:entity()
local name = debug.info(callback, "n") world:set(system, System, { callback = callback, name = debug.info(callback, "n") })
world:set(system, System, { callback = callback, name = name }) local depends_on = DependsOn :: jecs.Entity
world:add(system, pair(DependsOn, phase)) local p: Entity = phase or Heartbeat
world:add(system, pair(depends_on, p))
return system return system
end end
function scheduler_new(w: World, components: { [string]: Entity })
world = w
Disabled = world:component()
System = world:component()
Phase = world:component()
DependsOn = world:component()
Event = world:component()
RenderStepped = world:component()
Heartbeat = world:component()
PreSimulation = world:component()
PreAnimation = world:component()
local RunService = game:GetService("RunService")
if RunService:IsClient() then
world:add(RenderStepped, Phase)
world:set(RenderStepped, Event, RunService.RenderStepped)
end
world:add(Heartbeat, Phase)
world:set(Heartbeat, Event, RunService.Heartbeat)
world:add(PreSimulation, Phase)
world:set(PreSimulation, Event, RunService.PreSimulation)
world:add(PreAnimation, Phase)
world:set(PreAnimation, Event, RunService.PreAnimation)
for name, component in components do
world:set(component, Name, name)
end
table.insert(jabby.public, {
class_name = "World",
name = "MyWorld",
world = world,
debug = Name,
entities = {},
})
jabby.public.updated = true
scheduler = jabby.scheduler.create("scheduler")
table.insert(jabby.public, scheduler)
return { return {
phase = scheduler_phase_new, SYSTEM = scheduler_systems_new,
BEGIN = begin,
PHASE = scheduler_phase_new,
COLLECT = scheduler_collect_systems_all,
phases = { phases = {
RenderStepped = RenderStepped,
PreSimulation = PreSimulation,
Heartbeat = Heartbeat, Heartbeat = Heartbeat,
PreSimulation = PreSimulation,
PreAnimation = PreAnimation, PreAnimation = PreAnimation,
}, PreRender = PreRender
world = world,
components = {
DependsOn = DependsOn,
Disabled = Disabled,
Phase = Phase,
System = System,
},
collect = {
under_event = scheduler_collect_systems_under_event,
all = scheduler_collect_systems_all,
},
systems = {
new = scheduler_systems_new,
begin = begin,
},
} }
end
end
return {
new = scheduler_new,
} }

View file

@ -6,13 +6,12 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
local blink = require(game:GetService("ServerScriptService").net) local blink = require(game:GetService("ServerScriptService").net)
local jecs = require(ReplicatedStorage.ecs) local jecs = require(ReplicatedStorage.ecs)
local __ = jecs.Wildcard local __ = jecs.Wildcard
local std = ReplicatedStorage.std
local ref = require(std.ref)
local interval = require(std.interval)
local std = require(ReplicatedStorage.std) local world = require(std.world)
local ref = std.ref local cts = require(std.components)
local interval = std.interval
local world: std.World = std.world
local cts = std.components
local Mob = cts.Mob local Mob = cts.Mob
local Transform = cts.Transform local Transform = cts.Transform
@ -20,13 +19,26 @@ local Velocity = cts.Velocity
local Player = cts.Player local Player = cts.Player
local Character = cts.Character local Character = cts.Character
local characters = world
:query(Character)
:with(Player)
:cached()
local moving_mobs = world
:query(Transform, Velocity)
:with(Mob)
:cached()
local function mobsMove(dt: number) local function mobsMove(dt: number)
local targets = {} local targets = {}
for _, character in world:query(Character):with(Player):iter() do
for _, character in characters do
table.insert(targets, (character.PrimaryPart :: Part).Position) table.insert(targets, (character.PrimaryPart :: Part).Position)
end end
for mob, transform, v in world:query(Transform, Velocity):with(Mob):iter() do for mob, transform, v in moving_mobs do
local cf = transform.new local cf = transform.new
local p = cf.Position local p = cf.Position
@ -59,15 +71,18 @@ local function spawnMobs()
local cf = CFrame.new(p) local cf = CFrame.new(p)
local v = 5 local v = 5
local id = ref():set(Velocity, v):set(Transform, { new = cf }):add(Mob).id() local e = world:entity()
world:set(e, Velocity, v)
world:set(e, Transform, { new = cf })
world:add(e, Mob)
blink.SpawnMob.FireAll(id, cf, v) blink.SpawnMob.FireAll(e, cf, v)
end end
end end
return function(scheduler: std.Scheduler) local scheduler = require(std.scheduler)
local phases = scheduler.phases
local system_new = scheduler.systems.new scheduler.SYSTEM(spawnMobs)
system_new(mobsMove, phases.Heartbeat) scheduler.SYSTEM(mobsMove)
system_new(spawnMobs, phases.Heartbeat)
end return 0

View file

@ -1,43 +1,40 @@
local Players = game:GetService("Players") local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
local std = require(ReplicatedStorage.std) local std = ReplicatedStorage.std
local ref = std.ref local ref = require(std.ref)
local collect = std.collect local collect = require(std.collect)
local cts = std.components local cts = require(std.components)
local world = require(std.world)
local Player = cts.Player local Player = cts.Player
local Character = cts.Character local Character = cts.Character
local playersAdded = collect(Players.PlayerAdded)
local playersRemoved = collect(Players.PlayerRemoving)
local world: std.World = std.world
local conn = {} local conn = {}
local function players() local function playersAdded(player: Player)
for _, player in playersAdded do local e = ref(player.UserId)
world:set(std.world:entity(), cts.Transform) world:set(e, Player, player)
local e = ref(player.UserId):set(Player, player)
local characterAdd = player.CharacterAdded local characterAdd = player.CharacterAdded
conn[e.id()] = characterAdd:Connect(function(rig) conn[e] = characterAdd:Connect(function(rig)
while rig.Parent ~= workspace do while rig.Parent ~= workspace do
task.wait() task.wait()
end end
e:set(Character, rig) world:set(e, Character, rig)
end) end)
end end
for _, player in playersRemoved do local function playersRemoved(player: Player)
local id = ref(player.UserId):clear().id() local e = ref(player.UserId)
conn[id]:Disconnect() world:clear(e)
conn[id] = nil local connection = conn[e]
end connection:Disconnect()
conn[e] = nil
end end
return function(scheduler: std.Scheduler) local scheduler = require(std.scheduler)
local phases = scheduler.phases local phases = require(std.phases)
local system_new = scheduler.systems.new scheduler.SYSTEM(playersAdded, phases.PlayerAdded)
system_new(players, phases.Heartbeat) scheduler.SYSTEM(playersRemoved, phases.PlayerRemoved)
end
return 0

View file

@ -1,20 +1,22 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
local blink = require(ReplicatedStorage.net) local blink = require(ReplicatedStorage.net)
local std = require(ReplicatedStorage.std) local std = ReplicatedStorage.std
local world = std.world local world = require(std.world)
local ref = std.ref local ref = require(std.ref)
local cts = std.components local cts = require(std.components)
local Model = cts.Model local Model = cts.Model
local Transform = cts.Transform local Transform = cts.Transform
local moving_models = world:query(Transform, Model):cached()
local function move(dt: number) local function move(dt: number)
for _, transform, model in world:query(Transform, Model):iter() do for _, transform, model in moving_models do
local cf = transform.new local cf = transform.new
if cf ~= transform.old then if cf ~= transform.old then
local origo = model.PrimaryPart.CFrame local part = model.PrimaryPart :: BasePart
model.PrimaryPart.CFrame = origo:Lerp(cf, 1) local origo = part.CFrame
part.CFrame = origo:Lerp(cf, 1)
transform.old = cf transform.old = cf
end end
end end
@ -22,8 +24,8 @@ end
local function syncTransforms() local function syncTransforms()
for _, id, cf in blink.UpdateTransform.Iter() do for _, id, cf in blink.UpdateTransform.Iter() do
local e = ref("server-" .. id) local e = ref("server-" .. tostring(id))
local transform = e:get(cts.Transform) local transform = world:get(e, Transform)
if not transform then if not transform then
continue continue
end end
@ -31,9 +33,9 @@ local function syncTransforms()
end end
end end
return function(scheduler: std.Scheduler) local scheduler = require(std.scheduler)
local phases = scheduler.phases
local system_new = scheduler.systems.new scheduler.SYSTEM(move)
system_new(move, phases.Heartbeat) scheduler.SYSTEM(syncTransforms)
system_new(syncTransforms, phases.RenderStepped)
end return 0

View file

@ -1,9 +1,9 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
local blink = require(ReplicatedStorage.net) local blink = require(ReplicatedStorage.net)
local std = require(ReplicatedStorage.std) local std = ReplicatedStorage.std
local ref = std.ref local ref = require(std.ref)
local world = std.world local world = require(std.world)
local cts = std.components local cts = require(std.components)
local function syncMobs() local function syncMobs()
for _, id, cf, vel in blink.SpawnMob.Iter() do for _, id, cf, vel in blink.SpawnMob.Iter() do
@ -16,16 +16,16 @@ local function syncMobs()
part.Parent = model part.Parent = model
model.Parent = workspace model.Parent = workspace
ref("server-" .. id) local e = ref("server-" .. tostring(id))
:set(cts.Transform, { new = cf, old = cf }) world:set(e, cts.Transform, { new = cf, old = cf })
:set(cts.Velocity, vel) world:set(e, cts.Velocity, vel)
:set(cts.Model, model) world:set(e, cts.Model, model)
:add(cts.Mob) world:add(e, cts.Mob)
end end
end end
return function(scheduler: std.Scheduler) local scheduler = require(std.scheduler)
local phases = scheduler.phases scheduler.SYSTEM(syncMobs)
local system_new = scheduler.systems.new
system_new(syncMobs, phases.RenderStepped) return 0
end

View file

@ -0,0 +1,44 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local std = ReplicatedStorage.std
local world = require(std.world)
local A = world:component()
local B = world:component()
local C = world:component()
local D = world:component()
local function flip()
return math.random() >= 0.15
end
for i = 1, 2^8 do
local e = world:entity()
if flip() then
world:set(e, A, true)
end
if flip() then
world:set(e, B, true)
end
if flip() then
world:set(e, C, true)
end
if flip() then
world:set(e, D, true)
end
end
local function uncached()
for _ in world:query(A, B, C, D) do
end
end
local q = world:query(A, B, C, D):cached()
local function cached()
for _ in q do
end
end
local scheduler = require(std.scheduler)
scheduler.SYSTEM(uncached)
scheduler.SYSTEM(cached)
return 0

View file

@ -263,7 +263,7 @@ local function query_match(query, archetype: Archetype)
end end
local without = query.filter_without local without = query.filter_without
if not without then if without then
for _, id in without do for _, id in without do
if records[id] then if records[id] then
return false return false
@ -1843,13 +1843,12 @@ local function query_cached(query: QueryInner)
return world_query_iter_next return world_query_iter_next
end end
return setmetatable(query, { local cached_query = query :: any
__index = { cached_query.archetypes = query_archetypes
archetypes = query_archetypes, cached_query.__iter = cached_query_iter
__iter = cached_query_iter, cached_query.iter = cached_query_iter
iter = cached_query_iter setmetatable(cached_query, cached_query)
} return cached_query
})
end end
local Query = {} local Query = {}