mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 09:30:03 +00:00
wuh
This commit is contained in:
parent
b3f8e2504e
commit
f1dc668214
4 changed files with 356 additions and 387 deletions
|
@ -3,4 +3,4 @@ wally = "upliftgames/wally@0.3.1"
|
||||||
rojo = "rojo-rbx/rojo@7.4.1"
|
rojo = "rojo-rbx/rojo@7.4.1"
|
||||||
stylua = "johnnymorganz/stylua@0.19.1"
|
stylua = "johnnymorganz/stylua@0.19.1"
|
||||||
selene = "kampfkarren/selene@0.26.1"
|
selene = "kampfkarren/selene@0.26.1"
|
||||||
wally-patch-package="Barocena/wally-patch-package@1.2.1"
|
wally-patch-package = "Barocena/wally-patch-package@1.2.1"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
--!native
|
--!native
|
||||||
|
|
||||||
local testkit = require('../testkit')
|
local testkit = require("../testkit")
|
||||||
local BENCH, START = testkit.benchmark()
|
local BENCH, START = testkit.benchmark()
|
||||||
local function TITLE(title: string)
|
local function TITLE(title: string)
|
||||||
print()
|
print()
|
||||||
print(testkit.color.white(title))
|
print(testkit.color.white(title))
|
||||||
end
|
end
|
||||||
|
|
||||||
local jecs = require("../mirror/init")
|
local jecs = require("../mirror/init")
|
||||||
|
@ -15,285 +15,285 @@ local oldMatter = require("../oldMatter")
|
||||||
local newMatter = require("../newMatter")
|
local newMatter = require("../newMatter")
|
||||||
type i53 = number
|
type i53 = number
|
||||||
|
|
||||||
do TITLE (testkit.color.white_underline("Jecs query"))
|
do
|
||||||
local ecs = jecs.World.new()
|
TITLE(testkit.color.white_underline("Jecs query"))
|
||||||
do TITLE "one component in common"
|
local ecs = jecs.World.new()
|
||||||
|
do
|
||||||
|
TITLE("one component in common")
|
||||||
|
|
||||||
local function view_bench(
|
local function view_bench(world: jecs.World, A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53)
|
||||||
world: jecs.World,
|
BENCH("1 component", function()
|
||||||
A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53
|
for _ in world:query(A) do
|
||||||
)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("1 component", function()
|
BENCH("2 component", function()
|
||||||
for _ in world:query(A) do end
|
for _ in world:query(A, B) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("2 component", function()
|
BENCH("4 component", function()
|
||||||
for _ in world:query(A, B) do end
|
for _ in world:query(A, B, C, D) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("4 component", function()
|
BENCH("8 component", function()
|
||||||
for _ in world:query(A, B, C, D) do
|
for _ in world:query(A, B, C, D, E, F, G, H) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
BENCH("8 component", function()
|
local D1 = ecs:component()
|
||||||
for _ in world:query(A, B, C, D, E, F, G, H) do end
|
local D2 = ecs:component()
|
||||||
end)
|
local D3 = ecs:component()
|
||||||
end
|
local D4 = ecs:component()
|
||||||
|
local D5 = ecs:component()
|
||||||
|
local D6 = ecs:component()
|
||||||
|
local D7 = ecs:component()
|
||||||
|
local D8 = ecs:component()
|
||||||
|
|
||||||
local D1 = ecs:component()
|
local function flip()
|
||||||
local D2 = ecs:component()
|
return math.random() >= 0.15
|
||||||
local D3 = ecs:component()
|
end
|
||||||
local D4 = ecs:component()
|
|
||||||
local D5 = ecs:component()
|
|
||||||
local D6 = ecs:component()
|
|
||||||
local D7 = ecs:component()
|
|
||||||
local D8 = ecs:component()
|
|
||||||
|
|
||||||
local function flip()
|
local added = 0
|
||||||
return math.random() >= 0.15
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = 0
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
for i = 1, 2^16-2 do
|
for i = 1, 2 ^ 16 - 2 do
|
||||||
local entity = ecs:entity()
|
local entity = ecs:entity()
|
||||||
|
|
||||||
local combination = ""
|
local combination = ""
|
||||||
|
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "B"
|
combination ..= "B"
|
||||||
ecs:set(entity, D2, {value = true})
|
ecs:set(entity, D2, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "C"
|
combination ..= "C"
|
||||||
ecs:set(entity, D3, { value = true })
|
ecs:set(entity, D3, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "D"
|
combination ..= "D"
|
||||||
ecs:set(entity, D4, { value = true})
|
ecs:set(entity, D4, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "E"
|
combination ..= "E"
|
||||||
ecs:set(entity, D5, { value = true})
|
ecs:set(entity, D5, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "F"
|
combination ..= "F"
|
||||||
ecs:set(entity, D6, {value = true})
|
ecs:set(entity, D6, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "G"
|
combination ..= "G"
|
||||||
ecs:set(entity, D7, { value = true})
|
ecs:set(entity, D7, {value = true})
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "H"
|
combination ..= "H"
|
||||||
ecs:set(entity, D8, {value = true})
|
ecs:set(entity, D8, {value = true})
|
||||||
|
end
|
||||||
|
|
||||||
end
|
if #combination == 7 then
|
||||||
|
added += 1
|
||||||
if #combination == 7 then
|
ecs:set(entity, D1, {value = true})
|
||||||
added += 1
|
end
|
||||||
ecs:set(entity, D1, { value = true})
|
|
||||||
end
|
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local a = 0
|
local a = 0
|
||||||
for _ in archetypes do a+= 1 end
|
for _ in archetypes do
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
|
||||||
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do TITLE(testkit.color.white_underline("OldMatter query"))
|
do
|
||||||
|
TITLE(testkit.color.white_underline("OldMatter query"))
|
||||||
|
|
||||||
local ecs = oldMatter.World.new()
|
local ecs = oldMatter.World.new()
|
||||||
local component = oldMatter.component
|
local component = oldMatter.component
|
||||||
|
|
||||||
do TITLE "one component in common"
|
do
|
||||||
local function view_bench(
|
TITLE("one component in common")
|
||||||
world: jecs.World,
|
local function view_bench(world: jecs.World, A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53)
|
||||||
A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53
|
BENCH("1 component", function()
|
||||||
)
|
for _ in world:query(A) do
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("1 component", function()
|
BENCH("2 component", function()
|
||||||
for _ in world:query(A) do end
|
for _ in world:query(A, B) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("2 component", function()
|
BENCH("4 component", function()
|
||||||
for _ in world:query(A, B) do end
|
for _ in world:query(A, B, C, D) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("4 component", function()
|
BENCH("8 component", function()
|
||||||
for _ in world:query(A, B, C, D) do
|
for _ in world:query(A, B, C, D, E, F, G, H) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
BENCH("8 component", function()
|
local D1 = component()
|
||||||
for _ in world:query(A, B, C, D, E, F, G, H) do end
|
local D2 = component()
|
||||||
end)
|
local D3 = component()
|
||||||
end
|
local D4 = component()
|
||||||
|
local D5 = component()
|
||||||
|
local D6 = component()
|
||||||
|
local D7 = component()
|
||||||
|
local D8 = component()
|
||||||
|
|
||||||
local D1 = component()
|
local function flip()
|
||||||
local D2 = component()
|
return math.random() >= 0.15
|
||||||
local D3 = component()
|
end
|
||||||
local D4 = component()
|
|
||||||
local D5 = component()
|
|
||||||
local D6 = component()
|
|
||||||
local D7 = component()
|
|
||||||
local D8 = component()
|
|
||||||
|
|
||||||
local function flip()
|
local added = 0
|
||||||
return math.random() >= 0.15
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = 0
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
for i = 1, 2^16-2 do
|
for i = 1, 2 ^ 16 - 2 do
|
||||||
local entity = ecs:spawn()
|
local entity = ecs:spawn()
|
||||||
|
|
||||||
local combination = ""
|
local combination = ""
|
||||||
|
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "B"
|
combination ..= "B"
|
||||||
ecs:insert(entity, D2({value = true}))
|
ecs:insert(entity, D2({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "C"
|
combination ..= "C"
|
||||||
ecs:insert(entity, D3({value = true}))
|
ecs:insert(entity, D3({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "D"
|
combination ..= "D"
|
||||||
ecs:insert(entity, D4({value = true}))
|
ecs:insert(entity, D4({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "E"
|
combination ..= "E"
|
||||||
ecs:insert(entity, D5({value = true}))
|
ecs:insert(entity, D5({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "F"
|
combination ..= "F"
|
||||||
ecs:insert(entity, D6({value = true}))
|
ecs:insert(entity, D6({value = true}))
|
||||||
|
end
|
||||||
|
if flip() then
|
||||||
|
combination ..= "G"
|
||||||
|
ecs:insert(entity, D7({value = true}))
|
||||||
|
end
|
||||||
|
if flip() then
|
||||||
|
combination ..= "H"
|
||||||
|
ecs:insert(entity, D8({value = true}))
|
||||||
|
end
|
||||||
|
|
||||||
end
|
if #combination == 7 then
|
||||||
if flip() then
|
added += 1
|
||||||
combination ..= "G"
|
ecs:insert(entity, D1({value = true}))
|
||||||
ecs:insert(entity, D7({value = true}))
|
end
|
||||||
|
|
||||||
end
|
|
||||||
if flip() then
|
|
||||||
combination ..= "H"
|
|
||||||
ecs:insert(entity, D8({value = true}))
|
|
||||||
end
|
|
||||||
|
|
||||||
if #combination == 7 then
|
|
||||||
added += 1
|
|
||||||
ecs:insert(entity, D1({value = true}))
|
|
||||||
|
|
||||||
end
|
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local a = 0
|
local a = 0
|
||||||
for _ in archetypes do a+= 1 end
|
for _ in archetypes do
|
||||||
|
a += 1
|
||||||
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do TITLE(testkit.color.white_underline("NewMatter query"))
|
do
|
||||||
|
TITLE(testkit.color.white_underline("NewMatter query"))
|
||||||
|
|
||||||
local ecs = newMatter.World.new()
|
local ecs = newMatter.World.new()
|
||||||
local component = newMatter.component
|
local component = newMatter.component
|
||||||
|
|
||||||
do TITLE "one component in common"
|
do
|
||||||
local function view_bench(
|
TITLE("one component in common")
|
||||||
world: jecs.World,
|
local function view_bench(world: jecs.World, A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53)
|
||||||
A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53
|
BENCH("1 component", function()
|
||||||
)
|
for _ in world:query(A) do
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("1 component", function()
|
BENCH("2 component", function()
|
||||||
for _ in world:query(A) do end
|
for _ in world:query(A, B) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("2 component", function()
|
BENCH("4 component", function()
|
||||||
for _ in world:query(A, B) do end
|
for _ in world:query(A, B, C, D) do
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
BENCH("4 component", function()
|
BENCH("8 component", function()
|
||||||
for _ in world:query(A, B, C, D) do
|
for _ in world:query(A, B, C, D, E, F, G, H) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
BENCH("8 component", function()
|
local D1 = component()
|
||||||
for _ in world:query(A, B, C, D, E, F, G, H) do end
|
local D2 = component()
|
||||||
end)
|
local D3 = component()
|
||||||
end
|
local D4 = component()
|
||||||
|
local D5 = component()
|
||||||
|
local D6 = component()
|
||||||
|
local D7 = component()
|
||||||
|
local D8 = component()
|
||||||
|
|
||||||
local D1 = component()
|
local function flip()
|
||||||
local D2 = component()
|
return math.random() >= 0.15
|
||||||
local D3 = component()
|
end
|
||||||
local D4 = component()
|
|
||||||
local D5 = component()
|
|
||||||
local D6 = component()
|
|
||||||
local D7 = component()
|
|
||||||
local D8 = component()
|
|
||||||
|
|
||||||
local function flip()
|
local added = 0
|
||||||
return math.random() >= 0.15
|
|
||||||
end
|
|
||||||
|
|
||||||
local added = 0
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
for i = 1, 2^16-2 do
|
for i = 1, 2 ^ 16 - 2 do
|
||||||
local entity = ecs:spawn()
|
local entity = ecs:spawn()
|
||||||
|
|
||||||
local combination = ""
|
local combination = ""
|
||||||
|
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "B"
|
combination ..= "B"
|
||||||
ecs:insert(entity, D2({value = true}))
|
ecs:insert(entity, D2({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "C"
|
combination ..= "C"
|
||||||
ecs:insert(entity, D3({value = true}))
|
ecs:insert(entity, D3({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "D"
|
combination ..= "D"
|
||||||
ecs:insert(entity, D4({value = true}))
|
ecs:insert(entity, D4({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "E"
|
combination ..= "E"
|
||||||
ecs:insert(entity, D5({value = true}))
|
ecs:insert(entity, D5({value = true}))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "F"
|
combination ..= "F"
|
||||||
ecs:insert(entity, D6({value = true}))
|
ecs:insert(entity, D6({value = true}))
|
||||||
|
end
|
||||||
|
if flip() then
|
||||||
|
combination ..= "G"
|
||||||
|
ecs:insert(entity, D7({value = true}))
|
||||||
|
end
|
||||||
|
if flip() then
|
||||||
|
combination ..= "H"
|
||||||
|
ecs:insert(entity, D8({value = true}))
|
||||||
|
end
|
||||||
|
|
||||||
end
|
if #combination == 7 then
|
||||||
if flip() then
|
added += 1
|
||||||
combination ..= "G"
|
ecs:insert(entity, D1({value = true}))
|
||||||
ecs:insert(entity, D7({value = true}))
|
end
|
||||||
|
|
||||||
end
|
|
||||||
if flip() then
|
|
||||||
combination ..= "H"
|
|
||||||
ecs:insert(entity, D8({value = true}))
|
|
||||||
end
|
|
||||||
|
|
||||||
if #combination == 7 then
|
|
||||||
added += 1
|
|
||||||
ecs:insert(entity, D1({value = true}))
|
|
||||||
|
|
||||||
end
|
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local a = 0
|
local a = 0
|
||||||
for _ in archetypes do a+= 1 end
|
for _ in archetypes do
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
|
||||||
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
--!native
|
|
||||||
--!optimize 2
|
|
||||||
--!strict
|
|
||||||
|
|
||||||
export type i53 = number
|
|
||||||
export type i24 = number
|
|
||||||
|
|
||||||
export type Ty = {i53}
|
|
||||||
export type ArchetypeId = number
|
|
||||||
|
|
||||||
export type Column = {any}
|
|
||||||
|
|
||||||
export type Archetype = {
|
|
||||||
id: number,
|
|
||||||
edges: {
|
|
||||||
[i24]: {
|
|
||||||
add: Archetype,
|
|
||||||
remove: Archetype,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
types: Ty,
|
|
||||||
type: string | number,
|
|
||||||
entities: {number},
|
|
||||||
columns: {Column},
|
|
||||||
records: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Record = {
|
|
||||||
archetype: Archetype,
|
|
||||||
row: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EntityIndex = {[i24]: Record}
|
|
||||||
export type ComponentIndex = {[i24]: ArchetypeMap}
|
|
||||||
|
|
||||||
export type ArchetypeRecord = number
|
|
||||||
export type ArchetypeMap = {sparse: {[ArchetypeId]: ArchetypeRecord}, size: number}
|
|
||||||
export type Archetypes = {[ArchetypeId]: Archetype}
|
|
||||||
|
|
||||||
export type ArchetypeDiff = {
|
|
||||||
added: Ty,
|
|
||||||
removed: Ty,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Hook = {
|
|
||||||
ids: Ty,
|
|
||||||
archetype: Archetype,
|
|
||||||
otherArchetype: Archetype,
|
|
||||||
offset: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventDescription = Hook & {
|
|
||||||
event: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type World = {
|
|
||||||
archetypeIndex: {[number | string]: Archetype},
|
|
||||||
archetypes: {[number]: Archetype},
|
|
||||||
componentIndex: {[i24]: ArchetypeMap},
|
|
||||||
entityIndex: {[i53]: Record},
|
|
||||||
hooks: {[number]: {Hook}},
|
|
||||||
nextComponentId: number,
|
|
||||||
nextEntityId: number,
|
|
||||||
ROOT_ARCHETYPE: Archetype,
|
|
||||||
|
|
||||||
get: (self: World) -> (),
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WorldStatic = {
|
|
||||||
new: () -> World,
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
200
lib/init.lua
200
lib/init.lua
|
@ -3,29 +3,45 @@
|
||||||
--!strict
|
--!strict
|
||||||
--draft 4
|
--draft 4
|
||||||
|
|
||||||
local Types = require(script.Types)
|
type i53 = number
|
||||||
|
type i24 = number
|
||||||
|
|
||||||
type i53 = Types.i53
|
type Ty = {i53}
|
||||||
type i24 = Types.i24
|
type ArchetypeId = number
|
||||||
|
|
||||||
type Ty = Types.Ty
|
type Column = {any}
|
||||||
type ArchetypeId = Types.ArchetypeId
|
|
||||||
|
|
||||||
type Column = Types.Column
|
type Archetype = {
|
||||||
|
id: number,
|
||||||
|
edges: {
|
||||||
|
[i24]: {
|
||||||
|
add: Archetype,
|
||||||
|
remove: Archetype,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
types: Ty,
|
||||||
|
type: string | number,
|
||||||
|
entities: {number},
|
||||||
|
columns: {Column},
|
||||||
|
records: {},
|
||||||
|
}
|
||||||
|
|
||||||
type Archetype = Types.Archetype
|
type Record = {
|
||||||
type Record = Types.Record
|
archetype: Archetype,
|
||||||
|
row: number,
|
||||||
|
}
|
||||||
|
|
||||||
type EntityIndex = Types.EntityIndex
|
type EntityIndex = {[i24]: Record}
|
||||||
type ComponentIndex = Types.ComponentIndex
|
type ComponentIndex = {[i24]: ArchetypeMap}
|
||||||
|
|
||||||
type ArchetypeRecord = Types.ArchetypeRecord
|
type ArchetypeRecord = number
|
||||||
type ArchetypeMap = Types.ArchetypeMap
|
type ArchetypeMap = {sparse: {[ArchetypeId]: ArchetypeRecord}, size: number}
|
||||||
type Archetypes = Types.Archetypes
|
type Archetypes = {[ArchetypeId]: Archetype}
|
||||||
|
|
||||||
type ArchetypeDiff = Types.ArchetypeDiff
|
type ArchetypeDiff = {
|
||||||
|
added: Ty,
|
||||||
-- type World = Types.World
|
removed: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
local HI_COMPONENT_ID = 256
|
local HI_COMPONENT_ID = 256
|
||||||
local ON_ADD = HI_COMPONENT_ID + 1
|
local ON_ADD = HI_COMPONENT_ID + 1
|
||||||
|
@ -66,24 +82,27 @@ local function transitionArchetype(
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Move the entity from the source to the destination archetype.
|
-- Move the entity from the source to the destination archetype.
|
||||||
destinationEntities[destinationRow] = sourceEntities[sourceRow]
|
local atSourceRow = sourceEntities[sourceRow]
|
||||||
entityIndex[sourceEntities[sourceRow]].row = destinationRow
|
destinationEntities[destinationRow] = atSourceRow
|
||||||
|
entityIndex[atSourceRow].row = destinationRow
|
||||||
|
|
||||||
-- Because we have swapped columns we now have to update the records
|
-- Because we have swapped columns we now have to update the records
|
||||||
-- corresponding to the entities' rows that were swapped.
|
-- corresponding to the entities' rows that were swapped.
|
||||||
local movedAway = #sourceEntities
|
local movedAway = #sourceEntities
|
||||||
if sourceRow ~= movedAway then
|
if sourceRow ~= movedAway then
|
||||||
sourceEntities[sourceRow] = sourceEntities[movedAway]
|
local atMovedAway = sourceEntities[movedAway]
|
||||||
entityIndex[sourceEntities[movedAway]].row = sourceRow
|
sourceEntities[sourceRow] = atMovedAway
|
||||||
|
entityIndex[atMovedAway].row = sourceRow
|
||||||
end
|
end
|
||||||
|
|
||||||
sourceEntities[movedAway] = nil
|
sourceEntities[movedAway] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetypeAppend(entity: i53, archetype: Archetype): i24
|
local function archetypeAppend(entity: number, archetype: Archetype): number
|
||||||
local entities = archetype.entities
|
local entities = archetype.entities
|
||||||
table.insert(entities, entity)
|
local length = #entities + 1
|
||||||
return #entities
|
entities[length] = entity
|
||||||
|
return length
|
||||||
end
|
end
|
||||||
|
|
||||||
local function newEntity(entityId: i53, record: Record, archetype: Archetype)
|
local function newEntity(entityId: i53, record: Record, archetype: Archetype)
|
||||||
|
@ -107,32 +126,34 @@ local function hash(arr): string | number
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archetype, from: Archetype?)
|
local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archetype, from: Archetype?)
|
||||||
local destinationCount = #to.types
|
|
||||||
local destinationIds = to.types
|
local destinationIds = to.types
|
||||||
|
local records = to.records
|
||||||
|
local id = to.id
|
||||||
|
|
||||||
for i = 1, destinationCount do
|
for i, destinationId in destinationIds do
|
||||||
local destinationId = destinationIds[i]
|
local archetypesMap = componentIndex[destinationId]
|
||||||
|
|
||||||
if not componentIndex[destinationId] then
|
if not archetypesMap then
|
||||||
componentIndex[destinationId] = {size = 0, sparse = {}}
|
archetypesMap = {size = 0, sparse = {}}
|
||||||
|
componentIndex[destinationId] = archetypesMap
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetypesMap = componentIndex[destinationId]
|
archetypesMap.sparse[id] = i
|
||||||
archetypesMap.sparse[to.id] = i
|
records[destinationId] = i
|
||||||
to.records[destinationId] = i
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetypeOf(world: World, types: {i24}, prev: Archetype?): Archetype
|
local function archetypeOf(world: World, types: {i24}, prev: Archetype?): Archetype
|
||||||
local ty = hash(types)
|
local ty = hash(types)
|
||||||
|
|
||||||
world.nextArchetypeId = (world.nextArchetypeId :: number) + 1
|
local id = world.nextArchetypeId + 1
|
||||||
local id = world.nextArchetypeId
|
world.nextArchetypeId = id
|
||||||
|
|
||||||
local columns = {} :: {any}
|
local length = #types
|
||||||
|
local columns = table.create(length) :: {any}
|
||||||
|
|
||||||
for _ in types do
|
for index in types do
|
||||||
table.insert(columns, {})
|
columns[index] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetype = {
|
local archetype = {
|
||||||
|
@ -146,7 +167,7 @@ local function archetypeOf(world: World, types: {i24}, prev: Archetype?): Archet
|
||||||
}
|
}
|
||||||
world.archetypeIndex[ty] = archetype
|
world.archetypeIndex[ty] = archetype
|
||||||
world.archetypes[id] = archetype
|
world.archetypes[id] = archetype
|
||||||
if #types > 0 then
|
if length > 0 then
|
||||||
createArchetypeRecords(world.componentIndex, archetype, prev)
|
createArchetypeRecords(world.componentIndex, archetype, prev)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -212,9 +233,7 @@ local function ensureArchetype(world: World, types, prev)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function findInsert(types: {i53}, toAdd: i53)
|
local function findInsert(types: {i53}, toAdd: i53)
|
||||||
local count = #types
|
for i, id in types do
|
||||||
for i = 1, count do
|
|
||||||
local id = types[i]
|
|
||||||
if id == toAdd then
|
if id == toAdd then
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
@ -222,7 +241,7 @@ local function findInsert(types: {i53}, toAdd: i53)
|
||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return count + 1
|
return #types + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local function findArchetypeWith(world: World, node: Archetype, componentId: i53)
|
local function findArchetypeWith(world: World, node: Archetype, componentId: i53)
|
||||||
|
@ -243,38 +262,48 @@ local function findArchetypeWith(world: World, node: Archetype, componentId: i53
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ensureEdge(archetype: Archetype, componentId: i53)
|
local function ensureEdge(archetype: Archetype, componentId: i53)
|
||||||
if not archetype.edges[componentId] then
|
local edges = archetype.edges
|
||||||
archetype.edges[componentId] = {} :: any
|
local edge = edges[componentId]
|
||||||
|
if not edge then
|
||||||
|
edge = {} :: any
|
||||||
|
edges[componentId] = edge
|
||||||
end
|
end
|
||||||
return archetype.edges[componentId]
|
return edge
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetypeTraverseAdd(world: World, componentId: i53, from: Archetype): Archetype
|
local function archetypeTraverseAdd(world: World, componentId: i53, from: Archetype): Archetype
|
||||||
if not from then
|
if not from then
|
||||||
-- If there was no source archetype then it should return the ROOT_ARCHETYPE
|
-- If there was no source archetype then it should return the ROOT_ARCHETYPE
|
||||||
if not world.ROOT_ARCHETYPE then
|
local ROOT_ARCHETYPE = world.ROOT_ARCHETYPE
|
||||||
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
if not ROOT_ARCHETYPE then
|
||||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
||||||
|
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE :: never
|
||||||
end
|
end
|
||||||
from = world.ROOT_ARCHETYPE
|
from = ROOT_ARCHETYPE
|
||||||
end
|
end
|
||||||
local edge = ensureEdge(from, componentId)
|
|
||||||
|
|
||||||
if not edge.add then
|
local edge = ensureEdge(from, componentId)
|
||||||
|
local add = edge.add
|
||||||
|
if not add then
|
||||||
-- Save an edge using the component ID to the archetype to allow
|
-- Save an edge using the component ID to the archetype to allow
|
||||||
-- faster traversals to adjacent archetypes.
|
-- faster traversals to adjacent archetypes.
|
||||||
edge.add = findArchetypeWith(world, from, componentId)
|
add = findArchetypeWith(world, from, componentId)
|
||||||
|
edge.add = add :: never
|
||||||
end
|
end
|
||||||
|
|
||||||
return edge.add
|
return add
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ensureRecord(entityIndex, entityId: i53): Record
|
local function ensureRecord(entityIndex, entityId: i53): Record
|
||||||
local id = entityId
|
local id = entityId
|
||||||
if not entityIndex[id] then
|
local record = entityIndex[id]
|
||||||
entityIndex[id] = {}
|
|
||||||
|
if not record then
|
||||||
|
record = {}
|
||||||
|
entityIndex[id] = record
|
||||||
end
|
end
|
||||||
return entityIndex[id] :: Record
|
|
||||||
|
return record :: Record
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
|
@ -310,27 +339,30 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
|
||||||
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
||||||
local edge = ensureEdge(from, componentId)
|
local edge = ensureEdge(from, componentId)
|
||||||
|
|
||||||
if not edge.remove then
|
local remove = edge.remove
|
||||||
|
if not remove then
|
||||||
local to = table.clone(from.types)
|
local to = table.clone(from.types)
|
||||||
table.remove(to, table.find(to, componentId))
|
table.remove(to, table.find(to, componentId))
|
||||||
edge.remove = ensureArchetype(world, to, from)
|
remove = ensureArchetype(world, to, from)
|
||||||
|
edge.remove = remove :: never
|
||||||
end
|
end
|
||||||
|
|
||||||
return edge.remove
|
return remove
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.remove(world: World, entityId: i53, componentId: i53)
|
function World.remove(world: World, entityId: i53, componentId: i53)
|
||||||
local record = ensureRecord(world.entityIndex, entityId)
|
local entityIndex = world.entityIndex
|
||||||
|
local record = ensureRecord(entityIndex, entityId)
|
||||||
local sourceArchetype = record.archetype
|
local sourceArchetype = record.archetype
|
||||||
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
||||||
|
|
||||||
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
|
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
|
||||||
moveEntity(world.entityIndex, entityId, record, destinationArchetype)
|
moveEntity(entityIndex, entityId, record, destinationArchetype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keeping the function as small as possible to enable inlining
|
-- Keeping the function as small as possible to enable inlining
|
||||||
local function get(componentIndex: {[i24]: ArchetypeMap}, record: Record, componentId: i24): number?
|
local function get(_componentIndex: {[i24]: ArchetypeMap}, record: Record, componentId: i24)
|
||||||
local archetype = record.archetype
|
local archetype = record.archetype
|
||||||
local archetypeRecord = archetype.records[componentId]
|
local archetypeRecord = archetype.records[componentId]
|
||||||
|
|
||||||
|
@ -364,7 +396,7 @@ function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function noop(self: Query, ...: i53): () -> (number, ...any)
|
local function noop(_self: Query, ...: i53): () -> (number, ...any)
|
||||||
return function() end :: any
|
return function() end :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -379,6 +411,8 @@ export type Query = typeof(EmptyQuery)
|
||||||
|
|
||||||
function World.query(world: World, ...: i53): Query
|
function World.query(world: World, ...: i53): Query
|
||||||
local compatibleArchetypes = {}
|
local compatibleArchetypes = {}
|
||||||
|
local length = 0
|
||||||
|
|
||||||
local components = {...}
|
local components = {...}
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
local queryLength = #components
|
local queryLength = #components
|
||||||
|
@ -390,7 +424,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
local firstArchetypeMap
|
local firstArchetypeMap
|
||||||
local componentIndex = world.componentIndex
|
local componentIndex = world.componentIndex
|
||||||
|
|
||||||
for i, componentId in components do
|
for _, componentId in components do
|
||||||
local map = componentIndex[componentId]
|
local map = componentIndex[componentId]
|
||||||
if not map then
|
if not map then
|
||||||
return EmptyQuery
|
return EmptyQuery
|
||||||
|
@ -419,7 +453,9 @@ function World.query(world: World, ...: i53): Query
|
||||||
if skip then
|
if skip then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
table.insert(compatibleArchetypes, {archetype, indices})
|
|
||||||
|
length += 1
|
||||||
|
compatibleArchetypes[length] = {archetype, indices}
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
|
@ -431,18 +467,18 @@ function World.query(world: World, ...: i53): Query
|
||||||
preparedQuery.__index = preparedQuery
|
preparedQuery.__index = preparedQuery
|
||||||
|
|
||||||
function preparedQuery:without(...)
|
function preparedQuery:without(...)
|
||||||
local components = {...}
|
local withoutComponents = {...}
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
for index = #compatibleArchetypes, 1, -1 do
|
||||||
local archetype = compatibleArchetypes[i][1]
|
local archetype = compatibleArchetypes[index][1]
|
||||||
local shouldRemove = false
|
local shouldRemove = false
|
||||||
for _, componentId in components do
|
for _, componentId in withoutComponents do
|
||||||
if archetype.records[componentId] then
|
if archetype.records[componentId] then
|
||||||
shouldRemove = true
|
shouldRemove = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if shouldRemove then
|
if shouldRemove then
|
||||||
table.remove(compatibleArchetypes, i)
|
table.remove(compatibleArchetypes, index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -460,18 +496,20 @@ function World.query(world: World, ...: i53): Query
|
||||||
function preparedQuery:__iter()
|
function preparedQuery:__iter()
|
||||||
return function()
|
return function()
|
||||||
local archetype = compatibleArchetype[1]
|
local archetype = compatibleArchetype[1]
|
||||||
local row = next(archetype.entities, lastRow)
|
local entities = archetype.entities
|
||||||
|
local row = next(entities, lastRow)
|
||||||
while row == nil do
|
while row == nil do
|
||||||
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
||||||
if lastArchetype == nil then
|
if lastArchetype == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
archetype = compatibleArchetype[1]
|
archetype = compatibleArchetype[1]
|
||||||
row = next(archetype.entities, row)
|
entities = archetype.entities
|
||||||
|
row = next(entities, row)
|
||||||
end
|
end
|
||||||
lastRow = row
|
lastRow = row
|
||||||
|
|
||||||
local entityId = archetype.entities[row :: number]
|
local entityId = entities[row :: number]
|
||||||
local columns = archetype.columns
|
local columns = archetype.columns
|
||||||
local tr = compatibleArchetype[2]
|
local tr = compatibleArchetype[2]
|
||||||
|
|
||||||
|
@ -519,8 +557,8 @@ function World.query(world: World, ...: i53): Query
|
||||||
columns[tr[8]][row]
|
columns[tr[8]][row]
|
||||||
end
|
end
|
||||||
|
|
||||||
for i in components do
|
for index in components do
|
||||||
queryOutput[i] = tr[i][row]
|
queryOutput[index] = tr[index][row]
|
||||||
end
|
end
|
||||||
|
|
||||||
return entityId, unpack(queryOutput, 1, queryLength)
|
return entityId, unpack(queryOutput, 1, queryLength)
|
||||||
|
@ -542,8 +580,9 @@ function World.component(world: World)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.entity(world: World)
|
function World.entity(world: World)
|
||||||
world.nextEntityId += 1
|
local nextEntityId = world.nextEntityId + 1
|
||||||
return world.nextEntityId + REST
|
world.nextEntityId = nextEntityId
|
||||||
|
return nextEntityId + REST
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.delete(world: World, entityId: i53)
|
function World.delete(world: World, entityId: i53)
|
||||||
|
@ -559,11 +598,12 @@ end
|
||||||
|
|
||||||
function World.observer(world: World, ...)
|
function World.observer(world: World, ...)
|
||||||
local componentIds = {...}
|
local componentIds = {...}
|
||||||
|
local hooks = world.hooks
|
||||||
|
|
||||||
return {
|
return {
|
||||||
event = function(event)
|
event = function(event)
|
||||||
local hook = world.hooks[event]
|
local hook = hooks[event]
|
||||||
world.hooks[event] = nil
|
hooks[event] = nil
|
||||||
|
|
||||||
local last, change
|
local last, change
|
||||||
return function()
|
return function()
|
||||||
|
@ -573,10 +613,11 @@ function World.observer(world: World, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
local matched = false
|
local matched = false
|
||||||
|
local ids = change.ids
|
||||||
|
|
||||||
while not matched do
|
while not matched do
|
||||||
local skip = false
|
local skip = false
|
||||||
for _, id in change.ids do
|
for _, id in ids do
|
||||||
if not table.find(componentIds, id) then
|
if not table.find(componentIds, id) then
|
||||||
skip = true
|
skip = true
|
||||||
break
|
break
|
||||||
|
@ -585,6 +626,7 @@ function World.observer(world: World, ...)
|
||||||
|
|
||||||
if skip then
|
if skip then
|
||||||
last, change = next(hook, last)
|
last, change = next(hook, last)
|
||||||
|
ids = change.ids
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue