mirror of
https://github.com/Ukendio/jecs.git
synced 2025-08-04 19:29:18 +00:00
Compare commits
2 commits
9c0cc30dd0
...
8fc5bab9af
Author | SHA1 | Date | |
---|---|---|---|
|
8fc5bab9af | ||
|
825a8248a7 |
5 changed files with 1311 additions and 1355 deletions
|
@ -25,10 +25,9 @@ local function observers_new(world, query, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
local entity_index = world.entity_index :: any
|
local entity_index = world.entity_index :: any
|
||||||
local sparse_array = entity_index.sparse_array
|
|
||||||
|
|
||||||
local function emplaced(entity, id, value)
|
local function emplaced(entity, id, value)
|
||||||
local r = sparse_array[jecs.ECS_ID(entity)]
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
if not r then
|
if not r then
|
||||||
return
|
return
|
||||||
|
@ -99,11 +98,10 @@ local function monitors_new(world, query, callback)
|
||||||
terms = ids
|
terms = ids
|
||||||
end
|
end
|
||||||
|
|
||||||
local entity_index = world.entity_index
|
local entity_index = world.entity_index :: any
|
||||||
local sparse_array = entity_index.sparse_array
|
|
||||||
|
|
||||||
local function emplaced(entity: jecs.Entity)
|
local function emplaced(entity: jecs.Entity)
|
||||||
local r = sparse_array[jecs.ECS_ID(entity::number)] :: jecs.Record
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
if not r then
|
if not r then
|
||||||
return
|
return
|
||||||
|
@ -117,7 +115,8 @@ local function monitors_new(world, query, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function removed(entity: jecs.Entity, component: jecs.Id)
|
local function removed(entity: jecs.Entity, component: jecs.Id)
|
||||||
local r = sparse_array[jecs.ECS_ID(entity::number)] :: jecs.Record
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
if not r then
|
if not r then
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
--!native
|
--!native
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
local Matter = require(ReplicatedStorage.DevPackages.matter)
|
||||||
|
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||||
local newWorld = Matter.World.new()
|
local newWorld = Matter.World.new()
|
||||||
|
|
||||||
local jecs = require(ReplicatedStorage.Lib:Clone())
|
local jecs = require(ReplicatedStorage.Lib:Clone())
|
||||||
|
@ -19,6 +20,15 @@ local A6 = Matter.component()
|
||||||
local A7 = Matter.component()
|
local A7 = Matter.component()
|
||||||
local A8 = Matter.component()
|
local A8 = Matter.component()
|
||||||
|
|
||||||
|
local B1 = ecr.component()
|
||||||
|
local B2 = ecr.component()
|
||||||
|
local B3 = ecr.component()
|
||||||
|
local B4 = ecr.component()
|
||||||
|
local B5 = ecr.component()
|
||||||
|
local B6 = ecr.component()
|
||||||
|
local B7 = ecr.component()
|
||||||
|
local B8 = ecr.component()
|
||||||
|
|
||||||
local D1 = ecs:component()
|
local D1 = ecs:component()
|
||||||
local D2 = ecs:component()
|
local D2 = ecs:component()
|
||||||
local D3 = ecs:component()
|
local D3 = ecs:component()
|
||||||
|
@ -37,6 +47,8 @@ local E6 = mcs:component()
|
||||||
local E7 = mcs:component()
|
local E7 = mcs:component()
|
||||||
local E8 = mcs:component()
|
local E8 = mcs:component()
|
||||||
|
|
||||||
|
local registry2 = ecr.registry()
|
||||||
|
|
||||||
local function flip()
|
local function flip()
|
||||||
return math.random() >= 0.25
|
return math.random() >= 0.25
|
||||||
end
|
end
|
||||||
|
@ -44,42 +56,37 @@ end
|
||||||
local N = 2 ^ 16 - 2
|
local N = 2 ^ 16 - 2
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
|
|
||||||
for i = 1, 1000 do
|
|
||||||
ecs:entity()
|
|
||||||
mcs:entity()
|
|
||||||
end
|
|
||||||
|
|
||||||
local hm = 0
|
local hm = 0
|
||||||
for i = 1, N do
|
for i = 1, N do
|
||||||
|
local id = registry2.create()
|
||||||
local combination = ""
|
local combination = ""
|
||||||
local n = newWorld:spawn()
|
local n = newWorld:spawn()
|
||||||
local entity = ecs:entity()
|
local entity = ecs:entity()
|
||||||
local m = mcs:entity()
|
local m = mcs:entity()
|
||||||
|
|
||||||
if flip() then
|
if flip() then
|
||||||
ecs:add(entity, math.random(1, 1000) + jecs.Rest + 1)
|
registry2:set(id, B1, { value = true })
|
||||||
mcs:add(m, math.random(1, 1000) + jecs.Rest + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if flip() then
|
|
||||||
ecs:set(entity, D1, { value = true })
|
ecs:set(entity, D1, { value = true })
|
||||||
newWorld:insert(n, A1({ value = true }))
|
newWorld:insert(n, A1({ value = true }))
|
||||||
mcs:set(m, E1, { value = 2 })
|
mcs:set(m, E1, { value = 2 })
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "B"
|
combination ..= "B"
|
||||||
|
registry2:set(id, B2, { value = true })
|
||||||
ecs:set(entity, D2, { value = true })
|
ecs:set(entity, D2, { value = true })
|
||||||
mcs:set(m, E2, { value = 2 })
|
mcs:set(m, E2, { value = 2 })
|
||||||
newWorld:insert(n, A2({ value = true }))
|
newWorld:insert(n, A2({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "C"
|
combination ..= "C"
|
||||||
|
registry2:set(id, B3, { value = true })
|
||||||
ecs:set(entity, D3, { value = true })
|
ecs:set(entity, D3, { value = true })
|
||||||
mcs:set(m, E3, { value = 2 })
|
mcs:set(m, E3, { value = 2 })
|
||||||
newWorld:insert(n, A3({ value = true }))
|
newWorld:insert(n, A3({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "D"
|
combination ..= "D"
|
||||||
|
registry2:set(id, B4, { value = true })
|
||||||
ecs:set(entity, D4, { value = true })
|
ecs:set(entity, D4, { value = true })
|
||||||
mcs:set(m, E4, { value = 2 })
|
mcs:set(m, E4, { value = 2 })
|
||||||
|
|
||||||
|
@ -87,6 +94,7 @@ for i = 1, N do
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "E"
|
combination ..= "E"
|
||||||
|
registry2:set(id, B5, { value = true })
|
||||||
ecs:set(entity, D5, { value = true })
|
ecs:set(entity, D5, { value = true })
|
||||||
mcs:set(m, E5, { value = 2 })
|
mcs:set(m, E5, { value = 2 })
|
||||||
|
|
||||||
|
@ -94,18 +102,21 @@ for i = 1, N do
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "F"
|
combination ..= "F"
|
||||||
|
registry2:set(id, B6, { value = true })
|
||||||
ecs:set(entity, D6, { value = true })
|
ecs:set(entity, D6, { value = true })
|
||||||
mcs:set(m, E6, { value = 2 })
|
mcs:set(m, E6, { value = 2 })
|
||||||
newWorld:insert(n, A6({ value = true }))
|
newWorld:insert(n, A6({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "G"
|
combination ..= "G"
|
||||||
|
registry2:set(id, B7, { value = true })
|
||||||
ecs:set(entity, D7, { value = true })
|
ecs:set(entity, D7, { value = true })
|
||||||
mcs:set(m, E7, { value = 2 })
|
mcs:set(m, E7, { value = 2 })
|
||||||
newWorld:insert(n, A7({ value = true }))
|
newWorld:insert(n, A7({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "H"
|
combination ..= "H"
|
||||||
|
registry2:set(id, B8, { value = true })
|
||||||
newWorld:insert(n, A8({ value = true }))
|
newWorld:insert(n, A8({ value = true }))
|
||||||
ecs:set(entity, D8, { value = true })
|
ecs:set(entity, D8, { value = true })
|
||||||
mcs:set(m, E8, { value = 2 })
|
mcs:set(m, E8, { value = 2 })
|
||||||
|
@ -123,15 +134,12 @@ print("TEST", hm)
|
||||||
|
|
||||||
local count = 0
|
local count = 0
|
||||||
|
|
||||||
for _, archetype in ecs:query(D2, D4):archetypes() do
|
for _, archetype in ecs:query(D2, D4, D6, D8):archetypes() do
|
||||||
count += 1
|
count += #archetype.entities
|
||||||
end
|
end
|
||||||
|
|
||||||
print(count)
|
print(count)
|
||||||
|
|
||||||
local mq = ecs:query(E2, E4, D6, D8, 500):cached()
|
|
||||||
local jq = ecs:query(D2, D4, D6, D8, 500):cached()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ParameterGenerator = function()
|
ParameterGenerator = function()
|
||||||
return
|
return
|
||||||
|
@ -149,24 +157,13 @@ return {
|
||||||
-- end,
|
-- end,
|
||||||
--
|
--
|
||||||
Mirror = function()
|
Mirror = function()
|
||||||
for _, archetype in mq:archetypes() do
|
for entityId, firstComponent in mcs:query(E2, E4, E6, E8) do
|
||||||
if mirror.query_match(mq, archetype) then
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- for entityId, firstComponent in mcs:query(E2, E4) do
|
|
||||||
-- end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Jecs = function()
|
Jecs = function()
|
||||||
for _, archetype in jq:archetypes() do
|
for entityId, firstComponent in ecs:query(D2, D4, D6, D8) do
|
||||||
if jecs.query_match(jq, archetype) then
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
-- for entityId, firstComponent in ecs:query(D2, D4) do
|
|
||||||
-- end
|
|
||||||
end,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
218
jecs.luau
218
jecs.luau
|
@ -21,7 +21,6 @@ export type Archetype = {
|
||||||
columns: { Column },
|
columns: { Column },
|
||||||
columns_map: { [Id]: Column },
|
columns_map: { [Id]: Column },
|
||||||
dead: boolean,
|
dead: boolean,
|
||||||
bloom_filter: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryInner = {
|
export type QueryInner = {
|
||||||
|
@ -31,12 +30,12 @@ export type QueryInner = {
|
||||||
filter_without: { i53 },
|
filter_without: { i53 },
|
||||||
next: () -> (number, ...any),
|
next: () -> (number, ...any),
|
||||||
world: World,
|
world: World,
|
||||||
bloom_filter: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Entity<T = any> = number | { __T: T }
|
export type Entity<T = any> = number | { __T: T }
|
||||||
export type Id<T = any> = number | { __T: T }
|
export type Id<T = any> = number | { __T: T }
|
||||||
export type Pair<P, O> = Id<P>
|
export type Pair<P, O> = Id<P>
|
||||||
|
type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
|
||||||
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
||||||
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
||||||
|
|
||||||
|
@ -49,13 +48,13 @@ export type Query<T...> = typeof(setmetatable(
|
||||||
cached: (self: Query<T...>) -> Query<T...>,
|
cached: (self: Query<T...>) -> Query<T...>,
|
||||||
},
|
},
|
||||||
{} :: {
|
{} :: {
|
||||||
__iter: Iter<T...>,
|
__iter: Iter<T...>
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
export type Observer = {
|
export type Observer = {
|
||||||
callback: (archetype: Archetype) -> (),
|
callback: (archetype: Archetype) -> (),
|
||||||
query: QueryInner,
|
query: QueryInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type World = {
|
export type World = {
|
||||||
|
@ -103,7 +102,7 @@ export type World = {
|
||||||
|
|
||||||
--- Returns whether the entity has the ID.
|
--- Returns whether the entity has the ID.
|
||||||
has: (<T, a>(World, Entity<T>, Id<a>) -> boolean)
|
has: (<T, a>(World, Entity<T>, Id<a>) -> boolean)
|
||||||
& (<T, a, b>(World, Entity<T>, Id<a>, Id<a>) -> boolean)
|
& (<T, a, b >(World, Entity<T>, Id<a>, Id<a>) -> boolean)
|
||||||
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> boolean)
|
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> boolean)
|
||||||
& <T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> boolean,
|
& <T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> boolean,
|
||||||
|
|
||||||
|
@ -127,28 +126,8 @@ export type World = {
|
||||||
& (<A, B, C, D>(World, Id<A>, Id<B>, Id<C>, Id<D>) -> Query<A, B, C, D>)
|
& (<A, B, C, D>(World, Id<A>, Id<B>, Id<C>, Id<D>) -> Query<A, B, C, D>)
|
||||||
& (<A, B, C, D, E>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>) -> Query<A, B, C, D, E>)
|
& (<A, B, C, D, E>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>) -> Query<A, B, C, D, E>)
|
||||||
& (<A, B, C, D, E, F>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>) -> Query<A, B, C, D, E, F>)
|
& (<A, B, C, D, E, F>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>) -> Query<A, B, C, D, E, F>)
|
||||||
& (<A, B, C, D, E, F, G>(
|
& (<A, B, C, D, E, F, G>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>) -> Query<A, B, C, D, E, F, G>)
|
||||||
World,
|
& (<A, B, C, D, E, F, G, H>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>, Id<H>, ...Id<any>) -> Query<A, B, C, D, E, F, G, H>)
|
||||||
Id<A>,
|
|
||||||
Id<B>,
|
|
||||||
Id<C>,
|
|
||||||
Id<D>,
|
|
||||||
Id<E>,
|
|
||||||
Id<F>,
|
|
||||||
Id<G>
|
|
||||||
) -> Query<A, B, C, D, E, F, G>)
|
|
||||||
& (<A, B, C, D, E, F, G, H>(
|
|
||||||
World,
|
|
||||||
Id<A>,
|
|
||||||
Id<B>,
|
|
||||||
Id<C>,
|
|
||||||
Id<D>,
|
|
||||||
Id<E>,
|
|
||||||
Id<F>,
|
|
||||||
Id<G>,
|
|
||||||
Id<H>,
|
|
||||||
...Id<any>
|
|
||||||
) -> Query<A, B, C, D, E, F, G, H>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Record = {
|
export type Record = {
|
||||||
|
@ -176,7 +155,7 @@ export type EntityIndex = {
|
||||||
alive_count: number,
|
alive_count: number,
|
||||||
max_id: number,
|
max_id: number,
|
||||||
range_begin: number?,
|
range_begin: number?,
|
||||||
range_end: number?,
|
range_end: number?
|
||||||
}
|
}
|
||||||
|
|
||||||
-- stylua: ignore start
|
-- stylua: ignore start
|
||||||
|
@ -205,8 +184,6 @@ local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12
|
||||||
local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13
|
local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13
|
||||||
local EcsRest = HI_COMPONENT_ID + 14
|
local EcsRest = HI_COMPONENT_ID + 14
|
||||||
|
|
||||||
-- stylua: ignore end
|
|
||||||
|
|
||||||
local NULL_ARRAY = table.freeze({}) :: Column
|
local NULL_ARRAY = table.freeze({}) :: Column
|
||||||
local NULL = newproxy(false)
|
local NULL = newproxy(false)
|
||||||
|
|
||||||
|
@ -308,7 +285,10 @@ local function ECS_PAIR_SECOND(e: i53): i24
|
||||||
return (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
|
return (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
|
||||||
end
|
end
|
||||||
|
|
||||||
local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record?
|
local function entity_index_try_get_any(
|
||||||
|
entity_index: EntityIndex,
|
||||||
|
entity: number
|
||||||
|
): Record?
|
||||||
local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)]
|
local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)]
|
||||||
|
|
||||||
if not r or r.dense == 0 then
|
if not r or r.dense == 0 then
|
||||||
|
@ -405,18 +385,10 @@ local function ecs_pair_second(world: World, e: i53)
|
||||||
return ecs_get_alive(world, obj)
|
return ecs_get_alive(world, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bloom_filter_check(bloom: number, filter: number): boolean
|
|
||||||
return (bloom % (filter * 2)) >= filter
|
|
||||||
end
|
|
||||||
|
|
||||||
local function query_match(query: QueryInner, archetype: Archetype)
|
local function query_match(query: QueryInner, archetype: Archetype)
|
||||||
local columns_map = archetype.columns_map
|
local columns_map = archetype.columns_map
|
||||||
local with = query.filter_with
|
local with = query.filter_with
|
||||||
|
|
||||||
if not bloom_filter_check(query.bloom_filter, archetype.bloom_filter) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, id in with do
|
for _, id in with do
|
||||||
if not columns_map[id] then
|
if not columns_map[id] then
|
||||||
return false
|
return false
|
||||||
|
@ -443,8 +415,13 @@ local function find_observers(world: World, event: Id, component: Id): { Observe
|
||||||
return cache[component] :: any
|
return cache[component] :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function archetype_move(
|
||||||
local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: i24, from: Archetype, src_row: i24)
|
entity_index: EntityIndex,
|
||||||
|
to: Archetype,
|
||||||
|
dst_row: i24,
|
||||||
|
from: Archetype,
|
||||||
|
src_row: i24
|
||||||
|
)
|
||||||
local src_columns = from.columns
|
local src_columns = from.columns
|
||||||
local dst_entities = to.entities
|
local dst_entities = to.entities
|
||||||
local src_entities = from.entities
|
local src_entities = from.entities
|
||||||
|
@ -497,21 +474,33 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row:
|
||||||
record2.row = src_row
|
record2.row = src_row
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_append(entity: Entity, archetype: Archetype): number
|
local function archetype_append(
|
||||||
|
entity: Entity,
|
||||||
|
archetype: Archetype
|
||||||
|
): number
|
||||||
local entities = archetype.entities
|
local entities = archetype.entities
|
||||||
local length = #entities + 1
|
local length = #entities + 1
|
||||||
entities[length] = entity
|
entities[length] = entity
|
||||||
return length
|
return length
|
||||||
end
|
end
|
||||||
|
|
||||||
local function new_entity(entity: Entity, record: Record, archetype: Archetype): Record
|
local function new_entity(
|
||||||
|
entity: Entity,
|
||||||
|
record: Record,
|
||||||
|
archetype: Archetype
|
||||||
|
): Record
|
||||||
local row = archetype_append(entity, archetype)
|
local row = archetype_append(entity, archetype)
|
||||||
record.archetype = archetype
|
record.archetype = archetype
|
||||||
record.row = row
|
record.row = row
|
||||||
return record
|
return record
|
||||||
end
|
end
|
||||||
|
|
||||||
local function entity_move(entity_index: EntityIndex, entity: Entity, record: Record, to: Archetype)
|
local function entity_move(
|
||||||
|
entity_index: EntityIndex,
|
||||||
|
entity: Entity,
|
||||||
|
record: Record,
|
||||||
|
to: Archetype
|
||||||
|
)
|
||||||
local sourceRow = record.row
|
local sourceRow = record.row
|
||||||
local from = record.archetype
|
local from = record.archetype
|
||||||
local dst_row = archetype_append(entity, to)
|
local dst_row = archetype_append(entity, to)
|
||||||
|
@ -534,7 +523,8 @@ local function fetch(id: Id, columns_map: { [Entity]: Column }, row: number): an
|
||||||
return column[row]
|
return column[row]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_get(world: World, entity: Entity, a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
|
local function world_get(world: World, entity: Entity,
|
||||||
|
a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
|
||||||
local record = entity_index_try_get(world.entity_index, entity)
|
local record = entity_index_try_get(world.entity_index, entity)
|
||||||
if not record then
|
if not record then
|
||||||
return nil
|
return nil
|
||||||
|
@ -614,7 +604,8 @@ local function world_target(world: World, entity: Entity, relation: Id, index: n
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return entity_index_get_alive(entity_index, ECS_PAIR_SECOND(nth :: number))
|
return entity_index_get_alive(entity_index,
|
||||||
|
ECS_PAIR_SECOND(nth :: number))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
local function ECS_ID_IS_WILDCARD(e: i53): boolean
|
||||||
|
@ -649,9 +640,11 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord
|
||||||
local is_pair = ECS_IS_PAIR(id :: number)
|
local is_pair = ECS_IS_PAIR(id :: number)
|
||||||
if is_pair then
|
if is_pair then
|
||||||
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id :: number)) :: i53
|
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id :: number)) :: i53
|
||||||
ecs_assert(relation and entity_index_is_alive(entity_index, relation), ECS_INTERNAL_ERROR)
|
ecs_assert(relation and entity_index_is_alive(
|
||||||
|
entity_index, relation), ECS_INTERNAL_ERROR)
|
||||||
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number)) :: i53
|
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number)) :: i53
|
||||||
ecs_assert(target and entity_index_is_alive(entity_index, target), ECS_INTERNAL_ERROR)
|
ecs_assert(target and entity_index_is_alive(
|
||||||
|
entity_index, target), ECS_INTERNAL_ERROR)
|
||||||
end
|
end
|
||||||
|
|
||||||
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
||||||
|
@ -663,15 +656,21 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord
|
||||||
has_delete = true
|
has_delete = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local on_add, on_change, on_remove = world_get(world, relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
|
local on_add, on_change, on_remove = world_get(world,
|
||||||
|
relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
|
||||||
|
|
||||||
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
|
local is_tag = not world_has_one_inline(world,
|
||||||
|
relation, EcsComponent)
|
||||||
|
|
||||||
if is_tag and is_pair then
|
if is_tag and is_pair then
|
||||||
is_tag = not world_has_one_inline(world, target, EcsComponent)
|
is_tag = not world_has_one_inline(world, target, EcsComponent)
|
||||||
end
|
end
|
||||||
|
|
||||||
flags = bit32.bor(flags, if has_delete then ECS_ID_DELETE else 0, if is_tag then ECS_ID_IS_TAG else 0)
|
flags = bit32.bor(
|
||||||
|
flags,
|
||||||
|
if has_delete then ECS_ID_DELETE else 0,
|
||||||
|
if is_tag then ECS_ID_IS_TAG else 0
|
||||||
|
)
|
||||||
|
|
||||||
idr = {
|
idr = {
|
||||||
size = 0,
|
size = 0,
|
||||||
|
@ -739,15 +738,6 @@ local function archetype_register(world: World, archetype: Archetype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bloom_filter_add(filter: number, value: number): number
|
|
||||||
local shift = value % 53
|
|
||||||
local bit = 2 ^ shift
|
|
||||||
if filter % (bit * 2) < bit then
|
|
||||||
filter = filter + bit
|
|
||||||
end
|
|
||||||
return filter
|
|
||||||
end
|
|
||||||
|
|
||||||
local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?): Archetype
|
local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?): Archetype
|
||||||
local archetype_id = (world.max_archetype_id :: number) + 1
|
local archetype_id = (world.max_archetype_id :: number) + 1
|
||||||
world.max_archetype_id = archetype_id
|
world.max_archetype_id = archetype_id
|
||||||
|
@ -757,12 +747,6 @@ local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?):
|
||||||
|
|
||||||
local columns_map: { [Id]: Column } = {}
|
local columns_map: { [Id]: Column } = {}
|
||||||
|
|
||||||
local filter = 0
|
|
||||||
|
|
||||||
for _, id in id_types do
|
|
||||||
filter = bloom_filter_add(filter, id :: number)
|
|
||||||
end
|
|
||||||
|
|
||||||
local archetype: Archetype = {
|
local archetype: Archetype = {
|
||||||
columns = columns,
|
columns = columns,
|
||||||
columns_map = columns_map,
|
columns_map = columns_map,
|
||||||
|
@ -771,7 +755,6 @@ local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?):
|
||||||
type = ty,
|
type = ty,
|
||||||
types = id_types,
|
types = id_types,
|
||||||
dead = false,
|
dead = false,
|
||||||
bloom_filter = filter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
archetype_register(world, archetype)
|
archetype_register(world, archetype)
|
||||||
|
@ -810,7 +793,7 @@ local function world_range(world: World, range_begin: number, range_end: number?
|
||||||
for i = max_id + 1, range_begin do
|
for i = max_id + 1, range_begin do
|
||||||
dense_array[i] = i
|
dense_array[i] = i
|
||||||
sparse_array[i] = {
|
sparse_array[i] = {
|
||||||
dense = 0,
|
dense = 0
|
||||||
} :: Record
|
} :: Record
|
||||||
end
|
end
|
||||||
entity_index.max_id = range_begin - 1
|
entity_index.max_id = range_begin - 1
|
||||||
|
@ -849,7 +832,11 @@ local function find_insert(id_types: { i53 }, toAdd: i53): number
|
||||||
return #id_types + 1
|
return #id_types + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local function find_archetype_without(world: World, node: Archetype, id: Id): Archetype
|
local function find_archetype_without(
|
||||||
|
world: World,
|
||||||
|
node: Archetype,
|
||||||
|
id: Id
|
||||||
|
): Archetype
|
||||||
local id_types = node.types
|
local id_types = node.types
|
||||||
local at = table.find(id_types, id)
|
local at = table.find(id_types, id)
|
||||||
|
|
||||||
|
@ -859,7 +846,13 @@ local function find_archetype_without(world: World, node: Archetype, id: Id): Ar
|
||||||
return archetype_ensure(world, dst)
|
return archetype_ensure(world, dst)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function create_edge_for_remove(world: World, node: Archetype, edge: Map<Id, Archetype>, id: Id): Archetype
|
|
||||||
|
local function create_edge_for_remove(
|
||||||
|
world: World,
|
||||||
|
node: Archetype,
|
||||||
|
edge: Map<Id, Archetype>,
|
||||||
|
id: Id
|
||||||
|
): Archetype
|
||||||
local to = find_archetype_without(world, node, id)
|
local to = find_archetype_without(world, node, id)
|
||||||
local edges = world.archetype_edges
|
local edges = world.archetype_edges
|
||||||
local archetype_id = node.id
|
local archetype_id = node.id
|
||||||
|
@ -868,7 +861,11 @@ local function create_edge_for_remove(world: World, node: Archetype, edge: Map<I
|
||||||
return to
|
return to
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_traverse_remove(world: World, id: Id, from: Archetype): Archetype
|
local function archetype_traverse_remove(
|
||||||
|
world: World,
|
||||||
|
id: Id,
|
||||||
|
from: Archetype
|
||||||
|
): Archetype
|
||||||
local edges = world.archetype_edges
|
local edges = world.archetype_edges
|
||||||
local edge = edges[from.id]
|
local edge = edges[from.id]
|
||||||
|
|
||||||
|
@ -885,7 +882,7 @@ end
|
||||||
local function find_archetype_with(world: World, id: Id, from: Archetype): Archetype
|
local function find_archetype_with(world: World, id: Id, from: Archetype): Archetype
|
||||||
local id_types = from.types
|
local id_types = from.types
|
||||||
|
|
||||||
local at = find_insert(id_types :: { number }, id :: number)
|
local at = find_insert(id_types :: { number } , id :: number)
|
||||||
local dst = table.clone(id_types)
|
local dst = table.clone(id_types)
|
||||||
table.insert(dst, at, id)
|
table.insert(dst, at, id)
|
||||||
|
|
||||||
|
@ -922,6 +919,8 @@ local function world_component(world: World): i53
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function archetype_fast_delete_last(columns: { Column }, column_count: number)
|
local function archetype_fast_delete_last(columns: { Column }, column_count: number)
|
||||||
for i, column in columns do
|
for i, column in columns do
|
||||||
if column ~= NULL_ARRAY then
|
if column ~= NULL_ARRAY then
|
||||||
|
@ -978,6 +977,7 @@ local function archetype_delete(world: World, archetype: Archetype, row: number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function archetype_destroy(world: World, archetype: Archetype)
|
local function archetype_destroy(world: World, archetype: Archetype)
|
||||||
if archetype == world.ROOT_ARCHETYPE then
|
if archetype == world.ROOT_ARCHETYPE then
|
||||||
return
|
return
|
||||||
|
@ -1020,6 +1020,7 @@ end
|
||||||
|
|
||||||
local function NOOP() end
|
local function NOOP() end
|
||||||
|
|
||||||
|
|
||||||
local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
||||||
local world_query_iter_next
|
local world_query_iter_next
|
||||||
|
|
||||||
|
@ -1861,14 +1862,10 @@ local function world_query(world: World, ...)
|
||||||
world = world,
|
world = world,
|
||||||
}, Query)
|
}, Query)
|
||||||
|
|
||||||
local filter = 0
|
|
||||||
|
|
||||||
for _, id in ids do
|
for _, id in ids do
|
||||||
filter = bloom_filter_add(filter, id)
|
|
||||||
|
|
||||||
local map = component_index[id]
|
local map = component_index[id]
|
||||||
if not map then
|
if not map then
|
||||||
continue
|
return q
|
||||||
end
|
end
|
||||||
|
|
||||||
if idr == nil or (map.size :: number) < (idr.size :: number) then
|
if idr == nil or (map.size :: number) < (idr.size :: number) then
|
||||||
|
@ -1876,17 +1873,12 @@ local function world_query(world: World, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
q.bloom_filter = filter
|
|
||||||
|
|
||||||
if idr == nil then
|
if idr == nil then
|
||||||
return q
|
return q
|
||||||
end
|
end
|
||||||
|
|
||||||
for archetype_id in idr.records do
|
for archetype_id in idr.records do
|
||||||
local compatibleArchetype = archetypes[archetype_id]
|
local compatibleArchetype = archetypes[archetype_id]
|
||||||
if not bloom_filter_check(compatibleArchetype.bloom_filter, filter) then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
if #compatibleArchetype.entities == 0 then
|
if #compatibleArchetype.entities == 0 then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
@ -1948,7 +1940,7 @@ local function world_each<a>(world: World, id: Id<a>): () -> Entity
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_children<a>(world: World, parent: Id<a>)
|
local function world_children<a>(world: World, parent: Id<a>)
|
||||||
return world_each(world, ECS_PAIR(EcsChildOf, parent :: number))
|
return world_each(world, ECS_PAIR(EcsChildOf, parent::number))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_new()
|
local function world_new()
|
||||||
|
@ -1987,6 +1979,7 @@ local function world_new()
|
||||||
observable = observable,
|
observable = observable,
|
||||||
} :: World
|
} :: World
|
||||||
|
|
||||||
|
|
||||||
local ROOT_ARCHETYPE = archetype_create(world, {}, "")
|
local ROOT_ARCHETYPE = archetype_create(world, {}, "")
|
||||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
||||||
|
|
||||||
|
@ -2024,7 +2017,12 @@ local function world_new()
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_add<T, a>(world: World, entity: Entity<T>, id: Id<a>): ()
|
|
||||||
|
local function inner_world_add<T, a>(
|
||||||
|
world: World,
|
||||||
|
entity: Entity<T>,
|
||||||
|
id: Id<a>
|
||||||
|
): ()
|
||||||
local entity_index = world.entity_index
|
local entity_index = world.entity_index
|
||||||
local record = inner_entity_index_try_get(entity :: number)
|
local record = inner_entity_index_try_get(entity :: number)
|
||||||
if not record then
|
if not record then
|
||||||
|
@ -2052,8 +2050,9 @@ local function world_new()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_get(world: World, entity: Entity, a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
|
local function inner_world_get(world: World, entity: Entity,
|
||||||
local record = inner_entity_index_try_get(entity :: number)
|
a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
|
||||||
|
local record = inner_entity_index_try_get(entity::number)
|
||||||
if not record then
|
if not record then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
@ -2081,7 +2080,9 @@ local function world_new()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_has(world: World, entity: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
|
local function inner_world_has(world: World, entity: i53,
|
||||||
|
a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
|
||||||
|
|
||||||
local record = inner_entity_index_try_get(entity)
|
local record = inner_entity_index_try_get(entity)
|
||||||
if not record then
|
if not record then
|
||||||
return false
|
return false
|
||||||
|
@ -2094,11 +2095,11 @@ local function world_new()
|
||||||
|
|
||||||
local columns_map = archetype.columns_map
|
local columns_map = archetype.columns_map
|
||||||
|
|
||||||
return columns_map[a] ~= nil
|
return columns_map[a] ~= nil and
|
||||||
and (b == nil or columns_map[b] ~= nil)
|
(b == nil or columns_map[b] ~= nil) and
|
||||||
and (c == nil or columns_map[c] ~= nil)
|
(c == nil or columns_map[c] ~= nil) and
|
||||||
and (d == nil or columns_map[d] ~= nil)
|
(d == nil or columns_map[d] ~= nil) and
|
||||||
and (e == nil or error("args exceeded"))
|
(e == nil or error("args exceeded"))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_target<T, a>(world: World, entity: Entity<T>, relation: Id<a>, index: number?): Entity?
|
local function inner_world_target<T, a>(world: World, entity: Entity<T>, relation: Id<a>, index: number?): Entity?
|
||||||
|
@ -2112,7 +2113,7 @@ local function world_new()
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local r = ECS_PAIR(relation :: number, EcsWildcard)
|
local r = ECS_PAIR(relation::number, EcsWildcard)
|
||||||
local idr = world.component_index[r]
|
local idr = world.component_index[r]
|
||||||
|
|
||||||
if not idr then
|
if not idr then
|
||||||
|
@ -2137,7 +2138,8 @@ local function world_new()
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return entity_index_get_alive(world.entity_index, ECS_PAIR_SECOND(nth :: number))
|
return entity_index_get_alive(world.entity_index,
|
||||||
|
ECS_PAIR_SECOND(nth :: number))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_parent<T>(world: World, entity: Entity<T>): Entity?
|
local function inner_world_parent<T>(world: World, entity: Entity<T>): Entity?
|
||||||
|
@ -2239,7 +2241,7 @@ local function world_new()
|
||||||
return entity
|
return entity
|
||||||
else
|
else
|
||||||
for i = eindex_max_id + 1, index do
|
for i = eindex_max_id + 1, index do
|
||||||
eindex_sparse_array[i] = { dense = i } :: Record
|
eindex_sparse_array[i] = { dense = i } :: Record
|
||||||
eindex_dense_array[i] = i
|
eindex_dense_array[i] = i
|
||||||
end
|
end
|
||||||
entity_index.max_id = index
|
entity_index.max_id = index
|
||||||
|
@ -2291,10 +2293,10 @@ local function world_new()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function inner_world_clear<T>(world: World, entity: Entity<T>)
|
local function inner_world_clear<T>(world: World, entity: Entity<T>)
|
||||||
local tgt = ECS_PAIR(EcsWildcard, entity :: number)
|
local tgt = ECS_PAIR(EcsWildcard, entity::number)
|
||||||
local idr_t = component_index[tgt]
|
local idr_t = component_index[tgt]
|
||||||
local idr = component_index[entity]
|
local idr = component_index[entity]
|
||||||
local rel = ECS_PAIR(entity :: number, EcsWildcard)
|
local rel = ECS_PAIR(entity::number, EcsWildcard)
|
||||||
local idr_r = component_index[rel]
|
local idr_r = component_index[rel]
|
||||||
|
|
||||||
if idr then
|
if idr then
|
||||||
|
@ -2325,10 +2327,11 @@ local function world_new()
|
||||||
local removal_queued = false
|
local removal_queued = false
|
||||||
|
|
||||||
for _, id in idr_t_types do
|
for _, id in idr_t_types do
|
||||||
if not ECS_IS_PAIR(id :: number) then
|
if not ECS_IS_PAIR(id::number) then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
local object = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number))
|
local object = entity_index_get_alive(
|
||||||
|
entity_index, ECS_PAIR_SECOND(id::number))
|
||||||
if object ~= entity then
|
if object ~= entity then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
@ -2390,7 +2393,7 @@ local function world_new()
|
||||||
|
|
||||||
local function inner_world_delete<T>(world: World, entity: Entity<T>)
|
local function inner_world_delete<T>(world: World, entity: Entity<T>)
|
||||||
local entity_index = world.entity_index
|
local entity_index = world.entity_index
|
||||||
local record = inner_entity_index_try_get(entity :: number)
|
local record = inner_entity_index_try_get(entity::number)
|
||||||
if not record then
|
if not record then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -2406,11 +2409,11 @@ local function world_new()
|
||||||
|
|
||||||
local component_index = world.component_index
|
local component_index = world.component_index
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
local tgt = ECS_PAIR(EcsWildcard, entity :: number)
|
local tgt = ECS_PAIR(EcsWildcard, entity::number)
|
||||||
local rel = ECS_PAIR(entity :: number, EcsWildcard)
|
local rel = ECS_PAIR(entity::number, EcsWildcard)
|
||||||
|
|
||||||
local idr_t = component_index[tgt]
|
local idr_t = component_index[tgt]
|
||||||
local idr = component_index[entity :: number]
|
local idr = component_index[entity::number]
|
||||||
local idr_r = component_index[rel]
|
local idr_r = component_index[rel]
|
||||||
|
|
||||||
if idr then
|
if idr then
|
||||||
|
@ -2454,10 +2457,11 @@ local function world_new()
|
||||||
local removal_queued = false
|
local removal_queued = false
|
||||||
|
|
||||||
for _, id in idr_t_types do
|
for _, id in idr_t_types do
|
||||||
if not ECS_IS_PAIR(id :: number) then
|
if not ECS_IS_PAIR(id::number) then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
local object = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number))
|
local object = entity_index_get_alive(
|
||||||
|
entity_index, ECS_PAIR_SECOND(id::number))
|
||||||
if object ~= entity then
|
if object ~= entity then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
@ -2696,7 +2700,7 @@ return {
|
||||||
meta = (ECS_META :: any) :: <T>(id: Entity, id: Id<T>, value: T) -> Entity<T>,
|
meta = (ECS_META :: any) :: <T>(id: Entity, id: Id<T>, value: T) -> Entity<T>,
|
||||||
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
|
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
|
||||||
|
|
||||||
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
||||||
OnRemove = (EcsOnRemove :: any) :: Entity<(entity: Entity, id: Id) -> ()>,
|
OnRemove = (EcsOnRemove :: any) :: Entity<(entity: Entity, id: Id) -> ()>,
|
||||||
OnChange = (EcsOnChange :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
OnChange = (EcsOnChange :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
||||||
ChildOf = (EcsChildOf :: any) :: Entity,
|
ChildOf = (EcsChildOf :: any) :: Entity,
|
||||||
|
|
2373
mirror.luau
2373
mirror.luau
File diff suppressed because it is too large
Load diff
|
@ -4,4 +4,3 @@ rojo = "rojo-rbx/rojo@7.4.4"
|
||||||
stylua = "johnnymorganz/stylua@2.0.1"
|
stylua = "johnnymorganz/stylua@2.0.1"
|
||||||
Blink = "1Axen/Blink@0.14.1"
|
Blink = "1Axen/Blink@0.14.1"
|
||||||
wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
|
wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
|
||||||
StyLua = "JohnnyMorganz/StyLua@2.1.0"
|
|
||||||
|
|
Loading…
Reference in a new issue