mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 09:30:03 +00:00
parent
f82318c642
commit
e81b572480
45 changed files with 2867 additions and 2855 deletions
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
@ -17,4 +17,4 @@ jobs:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
version: latest # NOTE: we recommend pinning to a specific version in case of formatting changes
|
version: latest # NOTE: we recommend pinning to a specific version in case of formatting changes
|
||||||
# CLI arguments
|
# CLI arguments
|
||||||
args: ./src
|
args: --check ./src
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local mirror = require("../mirror/init")
|
local mirror = require("../mirror/init")
|
||||||
|
|
||||||
|
@ -39,36 +38,36 @@ do
|
||||||
|
|
||||||
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
|
if #combination == 7 then
|
||||||
added += 1
|
added += 1
|
||||||
ecs:set(entity, D1, {value = true})
|
ecs:set(entity, D1, { value = true })
|
||||||
end
|
end
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
@ -117,36 +116,36 @@ do
|
||||||
|
|
||||||
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
|
if #combination == 7 then
|
||||||
added += 1
|
added += 1
|
||||||
ecs:set(entity, D1, {value = true})
|
ecs:set(entity, D1, { value = true })
|
||||||
end
|
end
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,11 +8,12 @@ local function TITLE(s: string)
|
||||||
print(testkit.color.white(s))
|
print(testkit.color.white(s))
|
||||||
end
|
end
|
||||||
|
|
||||||
local N = 2^17
|
local N = 2 ^ 17
|
||||||
|
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
|
|
||||||
do TITLE "create"
|
do
|
||||||
|
TITLE("create")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
BENCH("entity", function()
|
BENCH("entity", function()
|
||||||
|
@ -29,10 +30,10 @@ do TITLE "create"
|
||||||
jecs.pair(A, B)
|
jecs.pair(A, B)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do TITLE "set"
|
do
|
||||||
|
TITLE("set")
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:entity()
|
local A = world:entity()
|
||||||
|
@ -65,7 +66,8 @@ end
|
||||||
-- we have a separate benchmark for relationships.
|
-- we have a separate benchmark for relationships.
|
||||||
-- this is due to that relationships have a very high id compared to normal
|
-- this is due to that relationships have a very high id compared to normal
|
||||||
-- components, which cause them to get added into the hashmap portion.
|
-- components, which cause them to get added into the hashmap portion.
|
||||||
do TITLE "set relationship"
|
do
|
||||||
|
TITLE("set relationship")
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:entity()
|
local A = world:entity()
|
||||||
|
@ -98,7 +100,8 @@ do TITLE "set relationship"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
do TITLE "get"
|
do
|
||||||
|
TITLE("get")
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -140,7 +143,8 @@ do TITLE "get"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
do TITLE "target"
|
do
|
||||||
|
TITLE("target")
|
||||||
|
|
||||||
BENCH("1st target", function()
|
BENCH("1st target", function()
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -164,14 +168,14 @@ do TITLE "target"
|
||||||
world:target(entities[i], A, 0)
|
world:target(entities[i], A, 0)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- this benchmark is used to view how fragmentation affects query performance
|
--- this benchmark is used to view how fragmentation affects query performance
|
||||||
--- we use this by determining how many entities should fit per arcehtype, instead
|
--- we use this by determining how many entities should fit per arcehtype, instead
|
||||||
--- of creating x amount of archetypes. this would scale better with any amount of
|
--- of creating x amount of archetypes. this would scale better with any amount of
|
||||||
--- entities.
|
--- entities.
|
||||||
do TITLE(`query {N} entities`)
|
do
|
||||||
|
TITLE(`query {N} entities`)
|
||||||
|
|
||||||
local function view_bench(n: number)
|
local function view_bench(n: number)
|
||||||
BENCH(`{n} entities per archetype`, function()
|
BENCH(`{n} entities per archetype`, function()
|
||||||
|
@ -194,7 +198,6 @@ do TITLE(`query {N} entities`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
START()
|
START()
|
||||||
for id in world:query(A, B, C, D) do
|
for id in world:query(A, B, C, D) do
|
||||||
end
|
end
|
||||||
|
@ -219,7 +222,6 @@ do TITLE(`query {N} entities`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
START()
|
START()
|
||||||
for _, archetype in world:query(A, B, C, D):archetypes() do
|
for _, archetype in world:query(A, B, C, D):archetypes() do
|
||||||
local columns, records = archetype.columns, archetype.records
|
local columns, records = archetype.columns, archetype.records
|
||||||
|
@ -235,7 +237,6 @@ do TITLE(`query {N} entities`)
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 13, 0, -1 do
|
for i = 13, 0, -1 do
|
||||||
view_bench(2^i)
|
view_bench(2 ^ i)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,36 +86,36 @@ do
|
||||||
|
|
||||||
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
|
if #combination == 7 then
|
||||||
added += 1
|
added += 1
|
||||||
ecs:set(entity, D1, {value = true})
|
ecs:set(entity, D1, { value = true })
|
||||||
end
|
end
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
@ -202,36 +202,36 @@ do
|
||||||
|
|
||||||
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
|
if #combination == 7 then
|
||||||
added += 1
|
added += 1
|
||||||
ecs:set(entity, D1, {value = true})
|
ecs:set(entity, D1, { value = true })
|
||||||
end
|
end
|
||||||
archetypes[combination] = true
|
archetypes[combination] = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,14 +12,13 @@ local ecs = jecs.World.new()
|
||||||
local A, B = Matter.component(), Matter.component()
|
local A, B = Matter.component(), Matter.component()
|
||||||
local C, D = ecs:component(), ecs:component()
|
local C, D = ecs:component(), ecs:component()
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ParameterGenerator = function()
|
ParameterGenerator = function()
|
||||||
local matter_entities = {}
|
local matter_entities = {}
|
||||||
local jecs_entities = {}
|
local jecs_entities = {}
|
||||||
local entities = {
|
local entities = {
|
||||||
matter = matter_entities,
|
matter = matter_entities,
|
||||||
jecs = jecs_entities
|
jecs = jecs_entities,
|
||||||
}
|
}
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
table.insert(matter_entities, newWorld:spawn(A(), B()))
|
table.insert(matter_entities, newWorld:spawn(A(), B()))
|
||||||
|
@ -29,20 +28,19 @@ return {
|
||||||
table.insert(jecs_entities, e)
|
table.insert(jecs_entities, e)
|
||||||
end
|
end
|
||||||
return entities
|
return entities
|
||||||
end;
|
end,
|
||||||
|
|
||||||
Functions = {
|
Functions = {
|
||||||
Matter = function(_, entities)
|
Matter = function(_, entities)
|
||||||
for _, entity in entities.matter do
|
for _, entity in entities.matter do
|
||||||
newWorld:despawn(entity)
|
newWorld:despawn(entity)
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
|
|
||||||
|
|
||||||
Jecs = function(_, entities)
|
Jecs = function(_, entities)
|
||||||
for _, entity in entities.jecs do
|
for _, entity in entities.jecs do
|
||||||
ecs:delete(entity)
|
ecs:delete(entity)
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
};
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||||
local jecs = require(ReplicatedStorage.Lib)
|
|
||||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||||
|
local jecs = require(ReplicatedStorage.Lib)
|
||||||
local newWorld = Matter.World.new()
|
local newWorld = Matter.World.new()
|
||||||
local ecs = jecs.World.new()
|
local ecs = jecs.World.new()
|
||||||
local mirror = require(ReplicatedStorage.mirror)
|
local mirror = require(ReplicatedStorage.mirror)
|
||||||
|
@ -45,7 +45,6 @@ local E6 = mcs:entity()
|
||||||
local E7 = mcs:entity()
|
local E7 = mcs:entity()
|
||||||
local E8 = mcs:entity()
|
local E8 = mcs:entity()
|
||||||
|
|
||||||
|
|
||||||
local registry2 = ecr.registry()
|
local registry2 = ecr.registry()
|
||||||
return {
|
return {
|
||||||
ParameterGenerator = function()
|
ParameterGenerator = function()
|
||||||
|
@ -56,7 +55,8 @@ return {
|
||||||
Matter = function()
|
Matter = function()
|
||||||
local e = newWorld:spawn()
|
local e = newWorld:spawn()
|
||||||
for i = 1, 5000 do
|
for i = 1, 5000 do
|
||||||
newWorld:insert(e,
|
newWorld:insert(
|
||||||
|
e,
|
||||||
A1({ value = true }),
|
A1({ value = true }),
|
||||||
A2({ value = true }),
|
A2({ value = true }),
|
||||||
A3({ value = true }),
|
A3({ value = true }),
|
||||||
|
@ -69,34 +69,31 @@ return {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
||||||
ECR = function()
|
ECR = function()
|
||||||
local e = registry2.create()
|
local e = registry2.create()
|
||||||
for i = 1, 5000 do
|
for i = 1, 5000 do
|
||||||
registry2:set(e, B1, {value = false})
|
registry2:set(e, B1, { value = false })
|
||||||
registry2:set(e, B2, {value = false})
|
registry2:set(e, B2, { value = false })
|
||||||
registry2:set(e, B3, {value = false})
|
registry2:set(e, B3, { value = false })
|
||||||
registry2:set(e, B4, {value = false})
|
registry2:set(e, B4, { value = false })
|
||||||
registry2:set(e, B5, {value = false})
|
registry2:set(e, B5, { value = false })
|
||||||
registry2:set(e, B6, {value = false})
|
registry2:set(e, B6, { value = false })
|
||||||
registry2:set(e, B7, {value = false})
|
registry2:set(e, B7, { value = false })
|
||||||
registry2:set(e, B8, {value = false})
|
registry2:set(e, B8, { value = false })
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
||||||
Jecs = function()
|
Jecs = function()
|
||||||
local e = ecs:entity()
|
local e = ecs:entity()
|
||||||
for i = 1, 5000 do
|
for i = 1, 5000 do
|
||||||
ecs:set(e, C1, {value = false})
|
ecs:set(e, C1, { value = false })
|
||||||
ecs:set(e, C2, {value = false})
|
ecs:set(e, C2, { value = false })
|
||||||
ecs:set(e, C3, {value = false})
|
ecs:set(e, C3, { value = false })
|
||||||
ecs:set(e, C4, {value = false})
|
ecs:set(e, C4, { value = false })
|
||||||
ecs:set(e, C5, {value = false})
|
ecs:set(e, C5, { value = false })
|
||||||
ecs:set(e, C6, {value = false})
|
ecs:set(e, C6, { value = false })
|
||||||
ecs:set(e, C7, {value = false})
|
ecs:set(e, C7, { value = false })
|
||||||
ecs:set(e, C8, {value = false})
|
ecs:set(e, C8, { value = false })
|
||||||
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
--!native
|
--!native
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local rgb = require(ReplicatedStorage.rgb)
|
|
||||||
local Matter = require(ReplicatedStorage.DevPackages["_Index"]["matter-ecs_matter@0.8.1"].matter)
|
local Matter = require(ReplicatedStorage.DevPackages["_Index"]["matter-ecs_matter@0.8.1"].matter)
|
||||||
local ecr = require(ReplicatedStorage.DevPackages["_Index"]["centau_ecr@0.8.0"].ecr)
|
local ecr = require(ReplicatedStorage.DevPackages["_Index"]["centau_ecr@0.8.0"].ecr)
|
||||||
|
local rgb = require(ReplicatedStorage.rgb)
|
||||||
local newWorld = Matter.World.new()
|
local newWorld = Matter.World.new()
|
||||||
|
|
||||||
local jecs = require(ReplicatedStorage.Lib)
|
local jecs = require(ReplicatedStorage.Lib)
|
||||||
|
@ -39,7 +39,6 @@ local D6 = ecs:component()
|
||||||
local D7 = ecs:component()
|
local D7 = ecs:component()
|
||||||
local D8 = ecs:component()
|
local D8 = ecs:component()
|
||||||
|
|
||||||
|
|
||||||
local E1 = mcs:entity()
|
local E1 = mcs:entity()
|
||||||
local E2 = mcs:entity()
|
local E2 = mcs:entity()
|
||||||
local E3 = mcs:entity()
|
local E3 = mcs:entity()
|
||||||
|
@ -49,14 +48,13 @@ local E6 = mcs:entity()
|
||||||
local E7 = mcs:entity()
|
local E7 = mcs:entity()
|
||||||
local E8 = mcs:entity()
|
local E8 = mcs:entity()
|
||||||
|
|
||||||
|
|
||||||
local registry2 = ecr.registry()
|
local registry2 = ecr.registry()
|
||||||
|
|
||||||
local function flip()
|
local function flip()
|
||||||
return math.random() >= 0.25
|
return math.random() >= 0.25
|
||||||
end
|
end
|
||||||
|
|
||||||
local N = 2^16-2
|
local N = 2 ^ 16 - 2
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
|
|
||||||
local hm = 0
|
local hm = 0
|
||||||
|
@ -68,66 +66,63 @@ for i = 1, N do
|
||||||
local m = mcs:entity()
|
local m = mcs:entity()
|
||||||
|
|
||||||
if flip() then
|
if flip() then
|
||||||
registry2:set(id, B1, {value = true})
|
registry2:set(id, B1, { value = true })
|
||||||
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})
|
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})
|
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})
|
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 })
|
||||||
|
|
||||||
newWorld:insert(n, A4({value = true}))
|
newWorld:insert(n, A4({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "E"
|
combination ..= "E"
|
||||||
registry2:set(id, B5, {value = true})
|
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 })
|
||||||
|
|
||||||
newWorld:insert(n, A5({value = true}))
|
newWorld:insert(n, A5({ value = true }))
|
||||||
end
|
end
|
||||||
if flip() then
|
if flip() then
|
||||||
combination ..= "F"
|
combination ..= "F"
|
||||||
registry2:set(id, B6, {value = true})
|
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})
|
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})
|
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 })
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if combination:find("BCDF") then
|
if combination:find("BCDF") then
|
||||||
if not archetypes[combination] then
|
if not archetypes[combination] then
|
||||||
print(combination)
|
print(combination)
|
||||||
|
|
|
@ -14,25 +14,25 @@ return {
|
||||||
local registry2 = ecr.registry()
|
local registry2 = ecr.registry()
|
||||||
|
|
||||||
return registry2
|
return registry2
|
||||||
end;
|
end,
|
||||||
|
|
||||||
Functions = {
|
Functions = {
|
||||||
Matter = function()
|
Matter = function()
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
newWorld:spawn()
|
newWorld:spawn()
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
|
|
||||||
ECR = function(_, registry2)
|
ECR = function(_, registry2)
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
registry2.create()
|
registry2.create()
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
|
|
||||||
Jecs = function()
|
Jecs = function()
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
ecs:entity()
|
ecs:entity()
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
};
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ if not RunService:IsClient() then
|
||||||
end
|
end
|
||||||
|
|
||||||
local Reliable: RemoteEvent = ReplicatedStorage:WaitForChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
local Reliable: RemoteEvent = ReplicatedStorage:WaitForChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
||||||
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:WaitForChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
local Unreliable: UnreliableRemoteEvent =
|
||||||
|
ReplicatedStorage:WaitForChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||||
|
|
||||||
local Invocations = 0
|
local Invocations = 0
|
||||||
|
|
||||||
|
@ -32,19 +33,19 @@ local RecieveInstanceCursor = 0
|
||||||
|
|
||||||
type Entry = {
|
type Entry = {
|
||||||
value: any,
|
value: any,
|
||||||
next: Entry?
|
next: Entry?,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Queue = {
|
type Queue = {
|
||||||
head: Entry?,
|
head: Entry?,
|
||||||
tail: Entry?
|
tail: Entry?,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BufferSave = {
|
type BufferSave = {
|
||||||
Size: number,
|
Size: number,
|
||||||
Cursor: number,
|
Cursor: number,
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Instances: {Instance}
|
Instances: { Instance },
|
||||||
}
|
}
|
||||||
|
|
||||||
local function Read(Bytes: number)
|
local function Read(Bytes: number)
|
||||||
|
@ -58,7 +59,7 @@ local function Save(): BufferSave
|
||||||
Size = SendSize,
|
Size = SendSize,
|
||||||
Cursor = SendCursor,
|
Cursor = SendCursor,
|
||||||
Buffer = SendBuffer,
|
Buffer = SendBuffer,
|
||||||
Instances = SendInstances
|
Instances = SendInstances,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ end
|
||||||
local function CreateQueue(): Queue
|
local function CreateQueue(): Queue
|
||||||
return {
|
return {
|
||||||
head = nil,
|
head = nil,
|
||||||
tail = nil
|
tail = nil,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ end
|
||||||
local function Push(queue: Queue, value: any)
|
local function Push(queue: Queue, value: any)
|
||||||
local entry: Entry = {
|
local entry: Entry = {
|
||||||
value = value,
|
value = value,
|
||||||
next = nil
|
next = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
if queue.tail ~= nil then
|
if queue.tail ~= nil then
|
||||||
|
@ -147,12 +148,12 @@ local Calls = table.create(256)
|
||||||
|
|
||||||
local Events: any = {
|
local Events: any = {
|
||||||
Reliable = table.create(256),
|
Reliable = table.create(256),
|
||||||
Unreliable = table.create(256)
|
Unreliable = table.create(256),
|
||||||
}
|
}
|
||||||
|
|
||||||
local Queue: any = {
|
local Queue: any = {
|
||||||
Reliable = table.create(256),
|
Reliable = table.create(256),
|
||||||
Unreliable = table.create(256)
|
Unreliable = table.create(256),
|
||||||
}
|
}
|
||||||
|
|
||||||
Queue.Unreliable[0] = CreateQueue()
|
Queue.Unreliable[0] = CreateQueue()
|
||||||
|
@ -220,7 +221,6 @@ function Types.WriteEVENT_SpawnMob(Value1: number, Value2: CFrame, Value3: numbe
|
||||||
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function StepReplication()
|
local function StepReplication()
|
||||||
if SendCursor <= 0 then
|
if SendCursor <= 0 then
|
||||||
return
|
return
|
||||||
|
@ -246,13 +246,13 @@ RunService.Heartbeat:Connect(function(DeltaTime: number)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
|
Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: { Instance })
|
||||||
RecieveCursor = 0
|
RecieveCursor = 0
|
||||||
RecieveBuffer = Buffer
|
RecieveBuffer = Buffer
|
||||||
RecieveInstances = Instances
|
RecieveInstances = Instances
|
||||||
RecieveInstanceCursor = 0
|
RecieveInstanceCursor = 0
|
||||||
local Size = buffer.len(RecieveBuffer)
|
local Size = buffer.len(RecieveBuffer)
|
||||||
while (RecieveCursor < Size) do
|
while RecieveCursor < Size do
|
||||||
-- Read BLOCK: 1 bytes
|
-- Read BLOCK: 1 bytes
|
||||||
local BLOCK_START = Read(1)
|
local BLOCK_START = Read(1)
|
||||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
@ -262,13 +262,13 @@ Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Unreliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
|
Unreliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: { Instance })
|
||||||
RecieveCursor = 0
|
RecieveCursor = 0
|
||||||
RecieveBuffer = Buffer
|
RecieveBuffer = Buffer
|
||||||
RecieveInstances = Instances
|
RecieveInstances = Instances
|
||||||
RecieveInstanceCursor = 0
|
RecieveInstanceCursor = 0
|
||||||
local Size = buffer.len(RecieveBuffer)
|
local Size = buffer.len(RecieveBuffer)
|
||||||
while (RecieveCursor < Size) do
|
while RecieveCursor < Size do
|
||||||
-- Read BLOCK: 1 bytes
|
-- Read BLOCK: 1 bytes
|
||||||
local BLOCK_START = Read(1)
|
local BLOCK_START = Read(1)
|
||||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
@ -285,7 +285,7 @@ return {
|
||||||
Iter = function(): () -> (number, number, CFrame)
|
Iter = function(): () -> (number, number, CFrame)
|
||||||
local index = 0
|
local index = 0
|
||||||
local queue = Queue.Unreliable[0]
|
local queue = Queue.Unreliable[0]
|
||||||
return function (): (number, number, CFrame)
|
return function(): (number, number, CFrame)
|
||||||
index += 1
|
index += 1
|
||||||
local arguments = Pop(queue)
|
local arguments = Pop(queue)
|
||||||
if arguments ~= nil then
|
if arguments ~= nil then
|
||||||
|
@ -297,7 +297,7 @@ return {
|
||||||
Next = function(): () -> (number, number, CFrame)
|
Next = function(): () -> (number, number, CFrame)
|
||||||
local index = 0
|
local index = 0
|
||||||
local queue = Queue.Unreliable[0]
|
local queue = Queue.Unreliable[0]
|
||||||
return function (): (number, number, CFrame)
|
return function(): (number, number, CFrame)
|
||||||
index += 1
|
index += 1
|
||||||
local arguments = Pop(queue)
|
local arguments = Pop(queue)
|
||||||
if arguments ~= nil then
|
if arguments ~= nil then
|
||||||
|
@ -305,13 +305,13 @@ return {
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
},
|
},
|
||||||
SpawnMob = {
|
SpawnMob = {
|
||||||
Iter = function(): () -> (number, number, CFrame, number)
|
Iter = function(): () -> (number, number, CFrame, number)
|
||||||
local index = 0
|
local index = 0
|
||||||
local queue = Queue.Reliable[0]
|
local queue = Queue.Reliable[0]
|
||||||
return function (): (number, number, CFrame, number)
|
return function(): (number, number, CFrame, number)
|
||||||
index += 1
|
index += 1
|
||||||
local arguments = Pop(queue)
|
local arguments = Pop(queue)
|
||||||
if arguments ~= nil then
|
if arguments ~= nil then
|
||||||
|
@ -323,7 +323,7 @@ return {
|
||||||
Next = function(): () -> (number, number, CFrame, number)
|
Next = function(): () -> (number, number, CFrame, number)
|
||||||
local index = 0
|
local index = 0
|
||||||
local queue = Queue.Reliable[0]
|
local queue = Queue.Reliable[0]
|
||||||
return function (): (number, number, CFrame, number)
|
return function(): (number, number, CFrame, number)
|
||||||
index += 1
|
index += 1
|
||||||
local arguments = Pop(queue)
|
local arguments = Pop(queue)
|
||||||
if arguments ~= nil then
|
if arguments ~= nil then
|
||||||
|
@ -331,7 +331,6 @@ return {
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
|
@ -22,7 +22,8 @@ if not Reliable then
|
||||||
Reliable = RemoteEvent
|
Reliable = RemoteEvent
|
||||||
end
|
end
|
||||||
|
|
||||||
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
local Unreliable: UnreliableRemoteEvent =
|
||||||
|
ReplicatedStorage:FindFirstChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||||
if not Unreliable then
|
if not Unreliable then
|
||||||
local UnreliableRemoteEvent = Instance.new("UnreliableRemoteEvent")
|
local UnreliableRemoteEvent = Instance.new("UnreliableRemoteEvent")
|
||||||
UnreliableRemoteEvent.Name = "BLINK_UNRELIABLE_REMOTE"
|
UnreliableRemoteEvent.Name = "BLINK_UNRELIABLE_REMOTE"
|
||||||
|
@ -46,19 +47,19 @@ local RecieveInstanceCursor = 0
|
||||||
|
|
||||||
type Entry = {
|
type Entry = {
|
||||||
value: any,
|
value: any,
|
||||||
next: Entry?
|
next: Entry?,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Queue = {
|
type Queue = {
|
||||||
head: Entry?,
|
head: Entry?,
|
||||||
tail: Entry?
|
tail: Entry?,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BufferSave = {
|
type BufferSave = {
|
||||||
Size: number,
|
Size: number,
|
||||||
Cursor: number,
|
Cursor: number,
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Instances: {Instance}
|
Instances: { Instance },
|
||||||
}
|
}
|
||||||
|
|
||||||
local function Read(Bytes: number)
|
local function Read(Bytes: number)
|
||||||
|
@ -72,7 +73,7 @@ local function Save(): BufferSave
|
||||||
Size = SendSize,
|
Size = SendSize,
|
||||||
Cursor = SendCursor,
|
Cursor = SendCursor,
|
||||||
Buffer = SendBuffer,
|
Buffer = SendBuffer,
|
||||||
Instances = SendInstances
|
Instances = SendInstances,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ end
|
||||||
local function CreateQueue(): Queue
|
local function CreateQueue(): Queue
|
||||||
return {
|
return {
|
||||||
head = nil,
|
head = nil,
|
||||||
tail = nil
|
tail = nil,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ end
|
||||||
local function Push(queue: Queue, value: any)
|
local function Push(queue: Queue, value: any)
|
||||||
local entry: Entry = {
|
local entry: Entry = {
|
||||||
value = value,
|
value = value,
|
||||||
next = nil
|
next = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
if queue.tail ~= nil then
|
if queue.tail ~= nil then
|
||||||
|
@ -161,15 +162,14 @@ local Calls = table.create(256)
|
||||||
|
|
||||||
local Events: any = {
|
local Events: any = {
|
||||||
Reliable = table.create(256),
|
Reliable = table.create(256),
|
||||||
Unreliable = table.create(256)
|
Unreliable = table.create(256),
|
||||||
}
|
}
|
||||||
|
|
||||||
local Queue: any = {
|
local Queue: any = {
|
||||||
Reliable = table.create(256),
|
Reliable = table.create(256),
|
||||||
Unreliable = table.create(256)
|
Unreliable = table.create(256),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Types.ReadEVENT_UpdateTransform(): (number, CFrame)
|
function Types.ReadEVENT_UpdateTransform(): (number, CFrame)
|
||||||
-- Read BLOCK: 32 bytes
|
-- Read BLOCK: 32 bytes
|
||||||
local BLOCK_START = Read(32)
|
local BLOCK_START = Read(32)
|
||||||
|
@ -232,8 +232,7 @@ function Types.WriteEVENT_SpawnMob(Value1: number, Value2: CFrame, Value3: numbe
|
||||||
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local PlayersMap: { [Player]: BufferSave } = {}
|
||||||
local PlayersMap: {[Player]: BufferSave} = {}
|
|
||||||
|
|
||||||
Players.PlayerRemoving:Connect(function(Player)
|
Players.PlayerRemoving:Connect(function(Player)
|
||||||
PlayersMap[Player] = nil
|
PlayersMap[Player] = nil
|
||||||
|
@ -258,26 +257,26 @@ end
|
||||||
|
|
||||||
RunService.Heartbeat:Connect(StepReplication)
|
RunService.Heartbeat:Connect(StepReplication)
|
||||||
|
|
||||||
Reliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
|
Reliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: { Instance })
|
||||||
RecieveCursor = 0
|
RecieveCursor = 0
|
||||||
RecieveBuffer = Buffer
|
RecieveBuffer = Buffer
|
||||||
RecieveInstances = Instances
|
RecieveInstances = Instances
|
||||||
RecieveInstanceCursor = 0
|
RecieveInstanceCursor = 0
|
||||||
local Size = buffer.len(RecieveBuffer)
|
local Size = buffer.len(RecieveBuffer)
|
||||||
while (RecieveCursor < Size) do
|
while RecieveCursor < Size do
|
||||||
-- Read BLOCK: 1 bytes
|
-- Read BLOCK: 1 bytes
|
||||||
local BLOCK_START = Read(1)
|
local BLOCK_START = Read(1)
|
||||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Unreliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
|
Unreliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: { Instance })
|
||||||
RecieveCursor = 0
|
RecieveCursor = 0
|
||||||
RecieveBuffer = Buffer
|
RecieveBuffer = Buffer
|
||||||
RecieveInstances = Instances
|
RecieveInstances = Instances
|
||||||
RecieveInstanceCursor = 0
|
RecieveInstanceCursor = 0
|
||||||
local Size = buffer.len(RecieveBuffer)
|
local Size = buffer.len(RecieveBuffer)
|
||||||
while (RecieveCursor < Size) do
|
while RecieveCursor < Size do
|
||||||
-- Read BLOCK: 1 bytes
|
-- Read BLOCK: 1 bytes
|
||||||
local BLOCK_START = Read(1)
|
local BLOCK_START = Read(1)
|
||||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
@ -302,7 +301,7 @@ return {
|
||||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||||
Unreliable:FireAllClients(Buffer, SendInstances)
|
Unreliable:FireAllClients(Buffer, SendInstances)
|
||||||
end,
|
end,
|
||||||
FireList = function(List: {Player}, Value1: number, Value2: CFrame): ()
|
FireList = function(List: { Player }, Value1: number, Value2: CFrame): ()
|
||||||
Load()
|
Load()
|
||||||
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
||||||
local Buffer = buffer.create(SendCursor)
|
local Buffer = buffer.create(SendCursor)
|
||||||
|
@ -342,7 +341,7 @@ return {
|
||||||
PlayersMap[Player] = Save()
|
PlayersMap[Player] = Save()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
FireList = function(List: {Player}, Value1: number, Value2: CFrame, Value3: number): ()
|
FireList = function(List: { Player }, Value1: number, Value2: CFrame, Value3: number): ()
|
||||||
Load()
|
Load()
|
||||||
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
||||||
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||||
|
@ -370,5 +369,4 @@ return {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
|
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 ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local jabby = require(ReplicatedStorage.Packages.jabby)
|
local jabby = require(ReplicatedStorage.Packages.jabby)
|
||||||
local std = require(ReplicatedStorage.std)
|
local std = require(ReplicatedStorage.std)
|
||||||
local Scheduler = std.Scheduler
|
local Scheduler = std.Scheduler
|
||||||
|
|
|
@ -34,7 +34,7 @@ end
|
||||||
local bt = {
|
local bt = {
|
||||||
SEQUENCE = SEQUENCE,
|
SEQUENCE = SEQUENCE,
|
||||||
FALLBACK = FALLBACK,
|
FALLBACK = FALLBACK,
|
||||||
RUNNING = RUNNING
|
RUNNING = RUNNING,
|
||||||
}
|
}
|
||||||
|
|
||||||
return bt
|
return bt
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
||||||
type World = jecs.World
|
type World = jecs.World
|
||||||
|
|
||||||
type Tracker<T> = { track: (world: World, fn: (changes: {
|
type Tracker<T> = {
|
||||||
|
track: (
|
||||||
|
world: World,
|
||||||
|
fn: (
|
||||||
|
changes: {
|
||||||
added: () -> () -> (number, T),
|
added: () -> () -> (number, T),
|
||||||
removed: () -> () -> number,
|
removed: () -> () -> number,
|
||||||
changed: () -> () -> (number, T, T)
|
changed: () -> () -> (number, T, T),
|
||||||
}) -> ()) -> ()
|
}
|
||||||
|
) -> ()
|
||||||
|
) -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Entity<T = any> = number & { __nominal_type_dont_use: T }
|
type Entity<T = any> = number & { __nominal_type_dont_use: T }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
||||||
local world = require(script.Parent.world)
|
local world = require(script.Parent.world)
|
||||||
|
|
||||||
type Entity<T=nil> = jecs.Entity<T>
|
type Entity<T = nil> = jecs.Entity<T>
|
||||||
local components: {
|
local components: {
|
||||||
Character: Entity<Model>,
|
Character: Entity<Model>,
|
||||||
Mob: Entity,
|
Mob: Entity,
|
||||||
|
@ -11,7 +11,8 @@ local components: {
|
||||||
Transform: Entity<CFrame>,
|
Transform: Entity<CFrame>,
|
||||||
Velocity: Entity<number>,
|
Velocity: Entity<number>,
|
||||||
Previous: Entity,
|
Previous: Entity,
|
||||||
} = {
|
} =
|
||||||
|
{
|
||||||
Character = world:component(),
|
Character = world:component(),
|
||||||
Mob = world:component(),
|
Mob = world:component(),
|
||||||
Model = world:component(),
|
Model = world:component(),
|
||||||
|
@ -20,6 +21,6 @@ local components: {
|
||||||
Transform = world:component(),
|
Transform = world:component(),
|
||||||
Velocity = world:component(),
|
Velocity = world:component(),
|
||||||
Previous = world:component(),
|
Previous = world:component(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return table.freeze(components)
|
return table.freeze(components)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
local world = require(script.Parent.world)
|
|
||||||
local handle = require(script.Parent.handle)
|
local handle = require(script.Parent.handle)
|
||||||
|
local world = require(script.Parent.world)
|
||||||
|
|
||||||
local singleton = world:entity()
|
local singleton = world:entity()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ type Handle = {
|
||||||
get: <T>(self: Handle, id: jecs.Entity<T>) -> T?,
|
get: <T>(self: Handle, id: jecs.Entity<T>) -> T?,
|
||||||
add: <T>(self: Handle, id: jecs.Entity<T>) -> Handle,
|
add: <T>(self: Handle, id: jecs.Entity<T>) -> Handle,
|
||||||
set: <T>(self: Handle, id: jecs.Entity<T>, value: T) -> Handle,
|
set: <T>(self: Handle, id: jecs.Entity<T>, value: T) -> Handle,
|
||||||
id: (self: Handle?) -> jecs.Entity
|
id: (self: Handle?) -> jecs.Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
local handle: (e: jecs.Entity) -> Handle
|
local handle: (e: jecs.Entity) -> Handle
|
||||||
|
|
|
@ -9,7 +9,7 @@ local function create_cache(hook)
|
||||||
local column = {}
|
local column = {}
|
||||||
self[component] = column
|
self[component] = column
|
||||||
return column
|
return column
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return function(world, component, fn)
|
return function(world, component, fn)
|
||||||
|
@ -26,7 +26,7 @@ end
|
||||||
local hooks = {
|
local hooks = {
|
||||||
OnSet = create_cache(jecs.OnSet),
|
OnSet = create_cache(jecs.OnSet),
|
||||||
OnAdd = create_cache(jecs.OnAdd),
|
OnAdd = create_cache(jecs.OnAdd),
|
||||||
OnRemove = create_cache(jecs.OnRemove)
|
OnRemove = create_cache(jecs.OnRemove),
|
||||||
}
|
}
|
||||||
|
|
||||||
return hooks
|
return hooks
|
||||||
|
|
|
@ -19,7 +19,7 @@ local std = {
|
||||||
world = world :: World,
|
world = world :: World,
|
||||||
pair = jecs.pair,
|
pair = jecs.pair,
|
||||||
__ = jecs.w,
|
__ = jecs.w,
|
||||||
hooks = require(script.hooks)
|
hooks = require(script.hooks),
|
||||||
}
|
}
|
||||||
|
|
||||||
return std
|
return std
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
local world = require(script.Parent.world)
|
|
||||||
local handle = require(script.Parent.handle)
|
local handle = require(script.Parent.handle)
|
||||||
|
local world = require(script.Parent.world)
|
||||||
local refs = {}
|
local refs = {}
|
||||||
|
|
||||||
local function fini(key)
|
local function fini(key)
|
||||||
|
|
|
@ -7,7 +7,7 @@ local pair = jecs.pair
|
||||||
local Name = jecs.Name
|
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 System = {
|
type System = {
|
||||||
callback: (world: World) -> (),
|
callback: (world: World) -> (),
|
||||||
|
@ -16,10 +16,9 @@ type System = {
|
||||||
|
|
||||||
type Systems = { System }
|
type Systems = { System }
|
||||||
|
|
||||||
|
|
||||||
type Events = {
|
type Events = {
|
||||||
RenderStepped: Systems,
|
RenderStepped: Systems,
|
||||||
Heartbeat: Systems
|
Heartbeat: Systems,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Scheduler = {
|
export type Scheduler = {
|
||||||
|
@ -27,17 +26,17 @@ export type Scheduler = {
|
||||||
Disabled: Entity,
|
Disabled: Entity,
|
||||||
System: Entity<System>,
|
System: Entity<System>,
|
||||||
Phase: Entity,
|
Phase: Entity,
|
||||||
DependsOn: Entity
|
DependsOn: Entity,
|
||||||
},
|
},
|
||||||
|
|
||||||
collect: {
|
collect: {
|
||||||
under_event: (event: Entity) -> Systems,
|
under_event: (event: Entity) -> Systems,
|
||||||
all: () -> Events
|
all: () -> Events,
|
||||||
},
|
},
|
||||||
|
|
||||||
systems: {
|
systems: {
|
||||||
begin: (events: Events) -> { [Entity]: thread },
|
begin: (events: Events) -> { [Entity]: thread },
|
||||||
new: (callback: (dt: number) -> (), phase: Entity) -> Entity
|
new: (callback: (dt: number) -> (), phase: Entity) -> Entity,
|
||||||
},
|
},
|
||||||
|
|
||||||
phases: {
|
phases: {
|
||||||
|
@ -73,7 +72,9 @@ do
|
||||||
local function run()
|
local function run()
|
||||||
local id = sys.id
|
local id = sys.id
|
||||||
local system_data = scheduler.system_data[id]
|
local system_data = scheduler.system_data[id]
|
||||||
if system_data.paused then return end
|
if system_data.paused then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
scheduler:mark_system_frame_start(id)
|
scheduler:mark_system_frame_start(id)
|
||||||
sys.callback(dt)
|
sys.callback(dt)
|
||||||
|
@ -87,7 +88,9 @@ do
|
||||||
local function begin(events: { Systems })
|
local function begin(events: { Systems })
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for event, systems in events do
|
for event, systems in events do
|
||||||
if not event then continue end
|
if not event then
|
||||||
|
continue
|
||||||
|
end
|
||||||
local event_name = tostring(event)
|
local event_name = tostring(event)
|
||||||
threads[event] = task.spawn(function()
|
threads[event] = task.spawn(function()
|
||||||
while true do
|
while true do
|
||||||
|
@ -97,7 +100,9 @@ do
|
||||||
for _, s in systems do
|
for _, s in systems do
|
||||||
sys = s
|
sys = s
|
||||||
local didNotYield, why = xpcall(function()
|
local didNotYield, why = xpcall(function()
|
||||||
for _ in run do break end
|
for _ in run do
|
||||||
|
break
|
||||||
|
end
|
||||||
end, debug.traceback)
|
end, debug.traceback)
|
||||||
|
|
||||||
if didNotYield then
|
if didNotYield then
|
||||||
|
@ -105,7 +110,8 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(why, "thread is not yieldable") then
|
if string.find(why, "thread is not yieldable") then
|
||||||
panic("Not allowed to yield in the systems."
|
panic(
|
||||||
|
"Not allowed to yield in the systems."
|
||||||
.. "\n"
|
.. "\n"
|
||||||
.. "System: "
|
.. "System: "
|
||||||
.. debug.info(s.callback, "n")
|
.. debug.info(s.callback, "n")
|
||||||
|
@ -128,9 +134,9 @@ do
|
||||||
table.insert(systems, {
|
table.insert(systems, {
|
||||||
id = scheduler:register_system({
|
id = scheduler:register_system({
|
||||||
name = s.name,
|
name = s.name,
|
||||||
phase = phase_name
|
phase = phase_name,
|
||||||
}),
|
}),
|
||||||
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)) do
|
||||||
|
@ -205,10 +211,9 @@ do
|
||||||
name = "MyWorld",
|
name = "MyWorld",
|
||||||
world = world,
|
world = world,
|
||||||
debug = Name,
|
debug = Name,
|
||||||
entities = {}
|
entities = {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
jabby.public.updated = true
|
jabby.public.updated = true
|
||||||
scheduler = jabby.scheduler.create("scheduler")
|
scheduler = jabby.scheduler.create("scheduler")
|
||||||
|
|
||||||
|
@ -221,7 +226,7 @@ do
|
||||||
RenderStepped = RenderStepped,
|
RenderStepped = RenderStepped,
|
||||||
PreSimulation = PreSimulation,
|
PreSimulation = PreSimulation,
|
||||||
Heartbeat = Heartbeat,
|
Heartbeat = Heartbeat,
|
||||||
PreAnimation = PreAnimation
|
PreAnimation = PreAnimation,
|
||||||
},
|
},
|
||||||
|
|
||||||
world = world,
|
world = world,
|
||||||
|
@ -235,17 +240,17 @@ do
|
||||||
|
|
||||||
collect = {
|
collect = {
|
||||||
under_event = scheduler_collect_systems_under_event,
|
under_event = scheduler_collect_systems_under_event,
|
||||||
all = scheduler_collect_systems_all
|
all = scheduler_collect_systems_all,
|
||||||
},
|
},
|
||||||
|
|
||||||
systems = {
|
systems = {
|
||||||
new = scheduler_systems_new,
|
new = scheduler_systems_new,
|
||||||
begin = begin
|
begin = begin,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
new = scheduler_new
|
new = scheduler_new,
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,7 @@ local function spawnMobs()
|
||||||
local cf = CFrame.new(p)
|
local cf = CFrame.new(p)
|
||||||
local v = 5
|
local v = 5
|
||||||
|
|
||||||
local id = ref()
|
local id = ref():set(Velocity, v):set(Transform, { new = cf }):add(Mob).id()
|
||||||
:set(Velocity, v)
|
|
||||||
:set(Transform, { new = cf })
|
|
||||||
:add(Mob)
|
|
||||||
.id()
|
|
||||||
|
|
||||||
blink.SpawnMob.FireAll(id, cf, v)
|
blink.SpawnMob.FireAll(id, cf, v)
|
||||||
end
|
end
|
||||||
|
@ -74,5 +70,4 @@ return function(scheduler: std.Scheduler)
|
||||||
local system_new = scheduler.systems.new
|
local system_new = scheduler.systems.new
|
||||||
system_new(mobsMove, phases.Heartbeat)
|
system_new(mobsMove, phases.Heartbeat)
|
||||||
system_new(spawnMobs, phases.Heartbeat)
|
system_new(spawnMobs, phases.Heartbeat)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,10 +17,7 @@ local conn = {}
|
||||||
|
|
||||||
local function players()
|
local function players()
|
||||||
for _, player in playersAdded do
|
for _, player in playersAdded do
|
||||||
world:set(
|
world:set(std.world:entity(), cts.Transform)
|
||||||
std.world:entity(),
|
|
||||||
cts.Transform
|
|
||||||
)
|
|
||||||
|
|
||||||
local e = ref(player.UserId):set(Player, player)
|
local e = ref(player.UserId):set(Player, player)
|
||||||
local characterAdd = player.CharacterAdded
|
local characterAdd = player.CharacterAdded
|
||||||
|
|
|
@ -22,7 +22,7 @@ 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-" .. id)
|
||||||
local transform = e:get(cts.Transform)
|
local transform = e:get(cts.Transform)
|
||||||
if not transform then
|
if not transform then
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -16,7 +16,7 @@ local function syncMobs()
|
||||||
part.Parent = model
|
part.Parent = model
|
||||||
model.Parent = workspace
|
model.Parent = workspace
|
||||||
|
|
||||||
ref("server-"..id)
|
ref("server-" .. id)
|
||||||
:set(cts.Transform, { new = cf, old = cf })
|
:set(cts.Transform, { new = cf, old = cf })
|
||||||
:set(cts.Velocity, vel)
|
:set(cts.Velocity, vel)
|
||||||
:set(cts.Model, model)
|
:set(cts.Model, model)
|
||||||
|
|
|
@ -23,14 +23,12 @@ print(`\{{pos.X}, {pos.Y}, {pos.Z}\}`)
|
||||||
-- Overwrite the value of the Position component
|
-- Overwrite the value of the Position component
|
||||||
world:set(bob, Position, Vector3.new(40, 50, 60))
|
world:set(bob, Position, Vector3.new(40, 50, 60))
|
||||||
|
|
||||||
|
|
||||||
local alice = world:entity()
|
local alice = world:entity()
|
||||||
-- Create another named entity
|
-- Create another named entity
|
||||||
world:set(alice, Name, "Alice")
|
world:set(alice, Name, "Alice")
|
||||||
world:set(alice, Position, Vector3.new(10, 20, 30))
|
world:set(alice, Position, Vector3.new(10, 20, 30))
|
||||||
world:add(alice, Walking)
|
world:add(alice, Walking)
|
||||||
|
|
||||||
|
|
||||||
-- Remove tag
|
-- Remove tag
|
||||||
world:remove(alice, Walking)
|
world:remove(alice, Walking)
|
||||||
|
|
||||||
|
|
|
@ -22,26 +22,14 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__add(left, right)
|
function Vector3.__add(left, right)
|
||||||
return Vector3.new(
|
return Vector3.new(left.X + right.X, left.Y + right.Y, left.Z + right.Z)
|
||||||
left.X + right.X,
|
|
||||||
left.Y + right.Y,
|
|
||||||
left.Z + right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__mul(left, right)
|
function Vector3.__mul(left, right)
|
||||||
if typeof(right) == "number" then
|
if typeof(right) == "number" then
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right, left.Y * right, left.Z * right)
|
||||||
left.X * right,
|
|
||||||
left.Y * right,
|
|
||||||
left.Z * right
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right.X, left.Y * right.Y, left.Z * right.Z)
|
||||||
left.X * right.X,
|
|
||||||
left.Y * right.Y,
|
|
||||||
left.Z * right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Vector3.one = Vector3.new(1, 1, 1)
|
Vector3.one = Vector3.new(1, 1, 1)
|
||||||
|
@ -107,7 +95,6 @@ do
|
||||||
world:add(mercury, Planet)
|
world:add(mercury, Planet)
|
||||||
world:set(mercury, Position, Vector3.one)
|
world:set(mercury, Position, Vector3.one)
|
||||||
|
|
||||||
|
|
||||||
iterate(sun, Vector3.zero)
|
iterate(sun, Vector3.zero)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,26 +18,14 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__add(left, right)
|
function Vector3.__add(left, right)
|
||||||
return Vector3.new(
|
return Vector3.new(left.X + right.X, left.Y + right.Y, left.Z + right.Z)
|
||||||
left.X + right.X,
|
|
||||||
left.Y + right.Y,
|
|
||||||
left.Z + right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__mul(left, right)
|
function Vector3.__mul(left, right)
|
||||||
if typeof(right) == "number" then
|
if typeof(right) == "number" then
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right, left.Y * right, left.Z * right)
|
||||||
left.X * right,
|
|
||||||
left.Y * right,
|
|
||||||
left.Z * right
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right.X, left.Y * right.Y, left.Z * right.Z)
|
||||||
left.X * right.X,
|
|
||||||
left.Y * right.Y,
|
|
||||||
left.Z * right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Vector3.one = Vector3.new(1, 1, 1)
|
Vector3.one = Vector3.new(1, 1, 1)
|
||||||
|
|
|
@ -2,11 +2,17 @@ local jecs = require("@jecs")
|
||||||
|
|
||||||
type World = jecs.WorldShim
|
type World = jecs.WorldShim
|
||||||
|
|
||||||
type Tracker<T> = { track: (world: World, fn: (changes: {
|
type Tracker<T> = {
|
||||||
|
track: (
|
||||||
|
world: World,
|
||||||
|
fn: (
|
||||||
|
changes: {
|
||||||
added: () -> () -> (number, T),
|
added: () -> () -> (number, T),
|
||||||
removed: () -> () -> number,
|
removed: () -> () -> number,
|
||||||
changed: () -> () -> (number, T, T)
|
changed: () -> () -> (number, T, T),
|
||||||
}) -> ()) -> ()
|
}
|
||||||
|
) -> ()
|
||||||
|
) -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
local function diff(a, b)
|
local function diff(a, b)
|
||||||
|
@ -139,26 +145,14 @@ do
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__add(left, right)
|
function Vector3.__add(left, right)
|
||||||
return Vector3.new(
|
return Vector3.new(left.X + right.X, left.Y + right.Y, left.Z + right.Z)
|
||||||
left.X + right.X,
|
|
||||||
left.Y + right.Y,
|
|
||||||
left.Z + right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector3.__mul(left, right)
|
function Vector3.__mul(left, right)
|
||||||
if typeof(right) == "number" then
|
if typeof(right) == "number" then
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right, left.Y * right, left.Z * right)
|
||||||
left.X * right,
|
|
||||||
left.Y * right,
|
|
||||||
left.Z * right
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
return Vector3.new(
|
return Vector3.new(left.X * right.X, left.Y * right.Y, left.Z * right.Z)
|
||||||
left.X * right.X,
|
|
||||||
left.Y * right.Y,
|
|
||||||
left.Z * right.Z
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Vector3.one = Vector3.new(1, 1, 1)
|
Vector3.one = Vector3.new(1, 1, 1)
|
||||||
|
|
|
@ -5,13 +5,13 @@ local __ = jecs.Wildcard
|
||||||
local Name = jecs.Name
|
local Name = jecs.Name
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
type Id<T=nil> = number & {__T: T}
|
type Id<T = nil> = number & { __T: T }
|
||||||
local Voxel = world:component() :: Id
|
local Voxel = world:component() :: Id
|
||||||
local Position = world:component() :: Id<Vector3>
|
local Position = world:component() :: Id<Vector3>
|
||||||
local Perception = world:component() :: Id<{
|
local Perception = world:component() :: Id<{
|
||||||
range: number,
|
range: number,
|
||||||
fov: number,
|
fov: number,
|
||||||
dir: Vector3
|
dir: Vector3,
|
||||||
}>
|
}>
|
||||||
local PrimaryPart = world:component() :: Id<Part>
|
local PrimaryPart = world:component() :: Id<Part>
|
||||||
|
|
||||||
|
@ -42,9 +42,7 @@ end
|
||||||
local map = {}
|
local map = {}
|
||||||
local grid = 50
|
local grid = 50
|
||||||
|
|
||||||
local function add_to_voxel(source: number, position: Vector3,
|
local function add_to_voxel(source: number, position: Vector3, prev_voxel_id: number?)
|
||||||
prev_voxel_id: number?)
|
|
||||||
|
|
||||||
local hash = position // grid
|
local hash = position // grid
|
||||||
local voxel_id = map[hash]
|
local voxel_id = map[hash]
|
||||||
if not voxel_id then
|
if not voxel_id then
|
||||||
|
@ -93,9 +91,7 @@ local function perceive_enemies(dt: number)
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_in_fov(self_position, target_position,
|
if is_in_fov(self_position, target_position, self_perception.dir, self_perception.fov) then
|
||||||
self_perception.dir, self_perception.fov) then
|
|
||||||
|
|
||||||
local p = target_position
|
local p = target_position
|
||||||
print(`Entity {world:get(e, Name)} can see target {world:get(enemy, Name)} at ({p.X}, {p.Y}, {p.Z})`)
|
print(`Entity {world:get(e, Name)} can see target {world:get(enemy, Name)} at ({p.X}, {p.Y}, {p.Z})`)
|
||||||
end
|
end
|
||||||
|
@ -106,7 +102,7 @@ local player = world:entity()
|
||||||
world:set(player, Perception, {
|
world:set(player, Perception, {
|
||||||
range = 100,
|
range = 100,
|
||||||
fov = 90,
|
fov = 90,
|
||||||
dir = Vector3.new(1, 0, 0)
|
dir = Vector3.new(1, 0, 0),
|
||||||
})
|
})
|
||||||
world:set(player, Name, "LocalPlayer")
|
world:set(player, Name, "LocalPlayer")
|
||||||
local primary_part = (local_player.Character :: Model).PrimaryPart :: Part
|
local primary_part = (local_player.Character :: Model).PrimaryPart :: Part
|
||||||
|
@ -117,11 +113,10 @@ local enemy = world:entity()
|
||||||
world:set(enemy, Name, "Enemy $1")
|
world:set(enemy, Name, "Enemy $1")
|
||||||
world:set(enemy, Position, Vector3.new(50, 0, 20))
|
world:set(enemy, Position, Vector3.new(50, 0, 20))
|
||||||
|
|
||||||
|
|
||||||
add_to_voxel(player, primary_part.Position)
|
add_to_voxel(player, primary_part.Position)
|
||||||
add_to_voxel(enemy, world)
|
add_to_voxel(enemy, world)
|
||||||
|
|
||||||
local dt = 1/60
|
local dt = 1 / 60
|
||||||
reconcile_client_owned_assembly_to_voxel(dt)
|
reconcile_client_owned_assembly_to_voxel(dt)
|
||||||
update_camera_direction(dt)
|
update_camera_direction(dt)
|
||||||
perceive_enemies(dt)
|
perceive_enemies(dt)
|
||||||
|
|
100
mirror/init.luau
100
mirror/init.luau
|
@ -6,10 +6,10 @@
|
||||||
type i53 = number
|
type i53 = number
|
||||||
type i24 = number
|
type i24 = number
|
||||||
|
|
||||||
type Ty = {i53}
|
type Ty = { i53 }
|
||||||
type ArchetypeId = number
|
type ArchetypeId = number
|
||||||
|
|
||||||
type Column = {any}
|
type Column = { any }
|
||||||
|
|
||||||
type Archetype = {
|
type Archetype = {
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -21,8 +21,8 @@ type Archetype = {
|
||||||
},
|
},
|
||||||
types: Ty,
|
types: Ty,
|
||||||
type: string | number,
|
type: string | number,
|
||||||
entities: {number},
|
entities: { number },
|
||||||
columns: {Column},
|
columns: { Column },
|
||||||
records: {},
|
records: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,12 +31,12 @@ type Record = {
|
||||||
row: number,
|
row: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntityIndex = {[i24]: Record}
|
type EntityIndex = { [i24]: Record }
|
||||||
type ComponentIndex = {[i24]: ArchetypeMap}
|
type ComponentIndex = { [i24]: ArchetypeMap }
|
||||||
|
|
||||||
type ArchetypeRecord = number
|
type ArchetypeRecord = number
|
||||||
type ArchetypeMap = {sparse: {[ArchetypeId]: ArchetypeRecord}, size: number}
|
type ArchetypeMap = { sparse: { [ArchetypeId]: ArchetypeRecord }, size: number }
|
||||||
type Archetypes = {[ArchetypeId]: Archetype}
|
type Archetypes = { [ArchetypeId]: Archetype }
|
||||||
|
|
||||||
type ArchetypeDiff = {
|
type ArchetypeDiff = {
|
||||||
added: Ty,
|
added: Ty,
|
||||||
|
@ -134,7 +134,7 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
||||||
local archetypesMap = componentIndex[destinationId]
|
local archetypesMap = componentIndex[destinationId]
|
||||||
|
|
||||||
if not archetypesMap then
|
if not archetypesMap then
|
||||||
archetypesMap = {size = 0, sparse = {}}
|
archetypesMap = { size = 0, sparse = {} }
|
||||||
componentIndex[destinationId] = archetypesMap
|
componentIndex[destinationId] = archetypesMap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -143,27 +143,27 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
||||||
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)
|
||||||
|
|
||||||
local id = world.nextArchetypeId + 1
|
local id = world.nextArchetypeId + 1
|
||||||
world.nextArchetypeId = id
|
world.nextArchetypeId = id
|
||||||
|
|
||||||
local length = #types
|
local length = #types
|
||||||
local columns = table.create(length) :: {any}
|
local columns = table.create(length) :: { any }
|
||||||
|
|
||||||
for index in types do
|
for index in types do
|
||||||
columns[index] = {}
|
columns[index] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetype = {
|
local archetype = {
|
||||||
columns = columns;
|
columns = columns,
|
||||||
edges = {};
|
edges = {},
|
||||||
entities = {};
|
entities = {},
|
||||||
id = id;
|
id = id,
|
||||||
records = {};
|
records = {},
|
||||||
type = ty;
|
type = ty,
|
||||||
types = types;
|
types = types,
|
||||||
}
|
}
|
||||||
world.archetypeIndex[ty] = archetype
|
world.archetypeIndex[ty] = archetype
|
||||||
world.archetypes[id] = archetype
|
world.archetypes[id] = archetype
|
||||||
|
@ -178,17 +178,17 @@ local World = {}
|
||||||
World.__index = World
|
World.__index = World
|
||||||
function World.new()
|
function World.new()
|
||||||
local self = setmetatable({
|
local self = setmetatable({
|
||||||
archetypeIndex = {};
|
archetypeIndex = {},
|
||||||
archetypes = {};
|
archetypes = {},
|
||||||
componentIndex = {};
|
componentIndex = {},
|
||||||
entityIndex = {};
|
entityIndex = {},
|
||||||
hooks = {
|
hooks = {
|
||||||
[ON_ADD] = {};
|
[ON_ADD] = {},
|
||||||
};
|
},
|
||||||
nextArchetypeId = 0;
|
nextArchetypeId = 0,
|
||||||
nextComponentId = 0;
|
nextComponentId = 0,
|
||||||
nextEntityId = 0;
|
nextEntityId = 0,
|
||||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype;
|
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||||
}, World)
|
}, World)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
@ -197,21 +197,21 @@ local function emit(world, eventDescription)
|
||||||
local event = eventDescription.event
|
local event = eventDescription.event
|
||||||
|
|
||||||
table.insert(world.hooks[event], {
|
table.insert(world.hooks[event], {
|
||||||
archetype = eventDescription.archetype;
|
archetype = eventDescription.archetype,
|
||||||
ids = eventDescription.ids;
|
ids = eventDescription.ids,
|
||||||
offset = eventDescription.offset;
|
offset = eventDescription.offset,
|
||||||
otherArchetype = eventDescription.otherArchetype;
|
otherArchetype = eventDescription.otherArchetype,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
||||||
if #added > 0 then
|
if #added > 0 then
|
||||||
emit(world, {
|
emit(world, {
|
||||||
archetype = archetype;
|
archetype = archetype,
|
||||||
event = ON_ADD;
|
event = ON_ADD,
|
||||||
ids = added;
|
ids = added,
|
||||||
offset = row;
|
offset = row,
|
||||||
otherArchetype = otherArchetype;
|
otherArchetype = otherArchetype,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -232,7 +232,7 @@ local function ensureArchetype(world: World, types, prev)
|
||||||
return archetypeOf(world, types, prev)
|
return archetypeOf(world, types, prev)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function findInsert(types: {i53}, toAdd: i53)
|
local function findInsert(types: { i53 }, toAdd: i53)
|
||||||
for i, id in types do
|
for i, id in types do
|
||||||
if id == toAdd then
|
if id == toAdd then
|
||||||
return -1
|
return -1
|
||||||
|
@ -326,7 +326,7 @@ function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
if #to.types > 0 then
|
if #to.types > 0 then
|
||||||
-- When there is no previous archetype it should create the archetype
|
-- When there is no previous archetype it should create the archetype
|
||||||
newEntity(entityId, record, to)
|
newEntity(entityId, record, to)
|
||||||
onNotifyAdd(world, to, from, record.row, {componentId})
|
onNotifyAdd(world, to, from, record.row, { componentId })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -401,8 +401,8 @@ local function noop(_self: Query, ...: i53): () -> (number, ...any)
|
||||||
end
|
end
|
||||||
|
|
||||||
local EmptyQuery = {
|
local EmptyQuery = {
|
||||||
__iter = noop;
|
__iter = noop,
|
||||||
without = noop;
|
without = noop,
|
||||||
}
|
}
|
||||||
EmptyQuery.__index = EmptyQuery
|
EmptyQuery.__index = EmptyQuery
|
||||||
setmetatable(EmptyQuery, EmptyQuery)
|
setmetatable(EmptyQuery, EmptyQuery)
|
||||||
|
@ -418,7 +418,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
local compatibleArchetypes = {}
|
local compatibleArchetypes = {}
|
||||||
local length = 0
|
local length = 0
|
||||||
|
|
||||||
local components = {...}
|
local components = { ... }
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
local queryLength = #components
|
local queryLength = #components
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
end
|
end
|
||||||
|
|
||||||
length += 1
|
length += 1
|
||||||
compatibleArchetypes[length] = {archetype, indices}
|
compatibleArchetypes[length] = { archetype, indices }
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
|
@ -468,7 +468,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
preparedQuery.__index = preparedQuery
|
preparedQuery.__index = preparedQuery
|
||||||
|
|
||||||
function preparedQuery:without(...)
|
function preparedQuery:without(...)
|
||||||
local withoutComponents = {...}
|
local withoutComponents = { ... }
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
for i = #compatibleArchetypes, 1, -1 do
|
||||||
local archetype = compatibleArchetypes[i][1]
|
local archetype = compatibleArchetypes[i][1]
|
||||||
local records = archetype.records
|
local records = archetype.records
|
||||||
|
@ -599,7 +599,7 @@ function World.delete(world: World, entityId: i53)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.observer(world: World, ...)
|
function World.observer(world: World, ...)
|
||||||
local componentIds = {...}
|
local componentIds = { ... }
|
||||||
local idsCount = #componentIds
|
local idsCount = #componentIds
|
||||||
local hooks = world.hooks
|
local hooks = world.hooks
|
||||||
|
|
||||||
|
@ -647,13 +647,13 @@ function World.observer(world: World, ...)
|
||||||
|
|
||||||
return archetype.entities[row], unpack(queryOutput, 1, idsCount)
|
return archetype.entities[row], unpack(queryOutput, 1, idsCount)
|
||||||
end
|
end
|
||||||
end;
|
end,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.freeze({
|
return table.freeze({
|
||||||
World = World;
|
World = World,
|
||||||
ON_ADD = ON_ADD;
|
ON_ADD = ON_ADD,
|
||||||
ON_REMOVE = ON_REMOVE;
|
ON_REMOVE = ON_REMOVE,
|
||||||
ON_SET = ON_SET;
|
ON_SET = ON_SET,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tools]
|
[tools]
|
||||||
wally = "upliftgames/wally@0.3.2"
|
wally = "upliftgames/wally@0.3.2"
|
||||||
rojo = "rojo-rbx/rojo@7.4.1"
|
rojo = "rojo-rbx/rojo@7.4.4"
|
||||||
stylua = "johnnymorganz/stylua@0.19.1"
|
stylua = "johnnymorganz/stylua@0.20.0"
|
||||||
selene = "kampfkarren/selene@0.26.1"
|
selene = "kampfkarren/selene@0.27.1"
|
||||||
Blink = "1Axen/Blink@0.14.1"
|
Blink = "1Axen/Blink@0.14.1"
|
||||||
|
|
|
@ -972,8 +972,6 @@ local function world_cleanup(world)
|
||||||
world.archetypeIndex = new_archetype_map
|
world.archetypeIndex = new_archetype_map
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local world_delete: (world: World, entity: i53, destruct: boolean?) -> ()
|
local world_delete: (world: World, entity: i53, destruct: boolean?) -> ()
|
||||||
do
|
do
|
||||||
function world_delete(world: World, entity: i53, destruct: boolean?)
|
function world_delete(world: World, entity: i53, destruct: boolean?)
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
column_width = 120
|
column_width = 120
|
||||||
|
line_endings = "Unix"
|
||||||
|
indent_type = "Tabs"
|
||||||
|
indent_width = 4
|
||||||
quote_style = "ForceDouble"
|
quote_style = "ForceDouble"
|
||||||
|
call_parentheses = "Always"
|
||||||
|
collapse_simple_statement = "Never"
|
||||||
|
|
||||||
[sort_requires]
|
[sort_requires]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
-- original author @centauri
|
-- original author @centauri
|
||||||
local bt
|
local bt
|
||||||
do
|
do
|
||||||
|
|
||||||
local FAILURE = 0
|
local FAILURE = 0
|
||||||
local SUCCESS = 1
|
local SUCCESS = 1
|
||||||
local RUNNING = 2
|
local RUNNING = 2
|
||||||
|
@ -40,17 +39,17 @@ end
|
||||||
local SEQUENCE, FALLBACK = bt.SEQUENCE, bt.FALLBACK
|
local SEQUENCE, FALLBACK = bt.SEQUENCE, bt.FALLBACK
|
||||||
local RUNNING, SUCCESS, FAILURE = bt.FAILURE, bt.SUCCESS, bt.FAILURE
|
local RUNNING, SUCCESS, FAILURE = bt.FAILURE, bt.SUCCESS, bt.FAILURE
|
||||||
|
|
||||||
local btree = FALLBACK {
|
local btree = FALLBACK({
|
||||||
SEQUENCE {
|
SEQUENCE({
|
||||||
function()
|
function()
|
||||||
return 1
|
return 1
|
||||||
end,
|
end,
|
||||||
|
|
||||||
function()
|
function()
|
||||||
return 0
|
return 0
|
||||||
end
|
end,
|
||||||
},
|
}),
|
||||||
SEQUENCE {
|
SEQUENCE({
|
||||||
function()
|
function()
|
||||||
print(3)
|
print(3)
|
||||||
local start = os.clock()
|
local start = os.clock()
|
||||||
|
@ -60,22 +59,25 @@ local btree = FALLBACK {
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end,
|
||||||
},
|
}),
|
||||||
function()
|
function()
|
||||||
return 1
|
return 1
|
||||||
end
|
end,
|
||||||
}
|
})
|
||||||
|
|
||||||
function wait(seconds)
|
function wait(seconds)
|
||||||
local start = os.clock()
|
local start = os.clock()
|
||||||
while os.clock() - start < seconds do end
|
while os.clock() - start < seconds do
|
||||||
|
end
|
||||||
return os.clock() - start
|
return os.clock() - start
|
||||||
end
|
end
|
||||||
|
|
||||||
local function panic(str)
|
local function panic(str)
|
||||||
-- We don't want to interrupt the loop when we error
|
-- We don't want to interrupt the loop when we error
|
||||||
coroutine.resume(coroutine.create(function() error(str) end))
|
coroutine.resume(coroutine.create(function()
|
||||||
|
error(str)
|
||||||
|
end))
|
||||||
end
|
end
|
||||||
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
|
@ -113,7 +115,8 @@ local function Scheduler(world, ...)
|
||||||
dt = sinceLastFrame
|
dt = sinceLastFrame
|
||||||
|
|
||||||
local didNotYield, why = xpcall(function()
|
local didNotYield, why = xpcall(function()
|
||||||
for _ in run do end
|
for _ in run do
|
||||||
|
end
|
||||||
end, debug.traceback)
|
end, debug.traceback)
|
||||||
|
|
||||||
if didNotYield then
|
if didNotYield then
|
||||||
|
@ -123,10 +126,7 @@ local function Scheduler(world, ...)
|
||||||
if string.find(why, "thread is not yieldable") then
|
if string.find(why, "thread is not yieldable") then
|
||||||
N -= 1
|
N -= 1
|
||||||
local name = table.remove(systems, i)
|
local name = table.remove(systems, i)
|
||||||
panic("Not allowed to yield in the systems."
|
panic("Not allowed to yield in the systems." .. "\n" .. `System: {name} has been ejected`)
|
||||||
.. "\n"
|
|
||||||
.. `System: {name} has been ejected`
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
panic(why)
|
panic(why)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ local function create_cache(hook)
|
||||||
local column = {}
|
local column = {}
|
||||||
self[component] = column
|
self[component] = column
|
||||||
return column
|
return column
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return function(world, component, fn)
|
return function(world, component, fn)
|
||||||
|
@ -23,7 +23,7 @@ end
|
||||||
local hooks = {
|
local hooks = {
|
||||||
OnSet = create_cache(jecs.OnSet),
|
OnSet = create_cache(jecs.OnSet),
|
||||||
OnAdd = create_cache(jecs.OnAdd),
|
OnAdd = create_cache(jecs.OnAdd),
|
||||||
OnRemove = create_cache(jecs.OnRemove)
|
OnRemove = create_cache(jecs.OnRemove),
|
||||||
}
|
}
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -38,10 +38,10 @@ hooks.OnSet(world, Position, function(entity, value)
|
||||||
order ..= "-$2"
|
order ..= "-$2"
|
||||||
end)
|
end)
|
||||||
|
|
||||||
world:set(world:entity(), Position, {x=1,y=0,z=1})
|
world:set(world:entity(), Position, { x = 1, y = 0, z = 1 })
|
||||||
|
|
||||||
-- Output:
|
-- Output:
|
||||||
-- $1 270 (1, 0, 1)
|
-- $1 270 (1, 0, 1)
|
||||||
-- $2 270 {1, 0, 1}
|
-- $2 270 {1, 0, 1}
|
||||||
|
|
||||||
assert(order == "$1".."-".."$2")
|
assert(order == "$1" .. "-" .. "$2")
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
local function calculateAverage(times)
|
local function calculateAverage(times)
|
||||||
local sum = 0
|
local sum = 0
|
||||||
for _, time in ipairs(times) do
|
for _, time in ipairs(times) do
|
||||||
|
@ -13,21 +12,23 @@ local CASES = {
|
||||||
jecs = function(world, ...)
|
jecs = function(world, ...)
|
||||||
for i = 1, 100 do
|
for i = 1, 100 do
|
||||||
local q = world:query(...)
|
local q = world:query(...)
|
||||||
for _ in q do end
|
for _ in q do
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mirror = function(world, ...)
|
mirror = function(world, ...)
|
||||||
for i = 1, 100 do
|
for i = 1, 100 do
|
||||||
local q = world:query(...)
|
local q = world:query(...)
|
||||||
for _ in q do end
|
for _ in q do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, fn in CASES do
|
for name, fn in CASES do
|
||||||
local times = {}
|
local times = {}
|
||||||
local allocations = {}
|
local allocations = {}
|
||||||
local ecs = require("@"..name)
|
local ecs = require("@" .. name)
|
||||||
local world = ecs.World.new()
|
local world = ecs.World.new()
|
||||||
local A, B, C = world:component(), world:component(), world:component()
|
local A, B, C = world:component(), world:component(), world:component()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
local testkit = require("@testkit")
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
|
local testkit = require("@testkit")
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
|
281
test/tests.luau
281
test/tests.luau
|
@ -1,5 +1,4 @@
|
||||||
|
local jecs: typeof(require("../jecs/src")) = require("@jecs")
|
||||||
local jecs: typeof(require("../jecs/src")) = require("@jecs");
|
|
||||||
|
|
||||||
local testkit = require("@testkit")
|
local testkit = require("@testkit")
|
||||||
local BENCH, START = testkit.benchmark()
|
local BENCH, START = testkit.benchmark()
|
||||||
|
@ -24,7 +23,7 @@ local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
||||||
CHECK(msg == s, 2)
|
CHECK(msg == s, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local N = 2^8
|
local N = 2 ^ 8
|
||||||
|
|
||||||
type World = jecs.WorldShim
|
type World = jecs.WorldShim
|
||||||
|
|
||||||
|
@ -154,7 +153,8 @@ TEST("world:cleanup()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:entity()", function()
|
TEST("world:entity()", function()
|
||||||
do CASE "unique IDs"
|
do
|
||||||
|
CASE("unique IDs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local set = {}
|
local set = {}
|
||||||
for i = 1, N do
|
for i = 1, N do
|
||||||
|
@ -163,7 +163,8 @@ TEST("world:entity()", function()
|
||||||
set[e] = true
|
set[e] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
do CASE "generations"
|
do
|
||||||
|
CASE("generations")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
CHECK(ECS_ID(e) == 1 + jecs.Rest)
|
CHECK(ECS_ID(e) == 1 + jecs.Rest)
|
||||||
|
@ -173,7 +174,8 @@ TEST("world:entity()", function()
|
||||||
CHECK(ECS_GENERATION(e) == 1) -- 1
|
CHECK(ECS_GENERATION(e) == 1) -- 1
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "pairs"
|
do
|
||||||
|
CASE("pairs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local _e = world:entity()
|
local _e = world:entity()
|
||||||
local e2 = world:entity()
|
local e2 = world:entity()
|
||||||
|
@ -191,7 +193,8 @@ TEST("world:entity()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:set()", function()
|
TEST("world:set()", function()
|
||||||
do CASE "archetype move"
|
do
|
||||||
|
CASE("archetype move")
|
||||||
do
|
do
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
@ -225,7 +228,8 @@ TEST("world:set()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "arbitrary order"
|
do
|
||||||
|
CASE("arbitrary order")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:entity()
|
||||||
|
@ -238,7 +242,8 @@ TEST("world:set()", function()
|
||||||
CHECK(world:get(id, Poison) == 5)
|
CHECK(world:get(id, Poison) == 5)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "pairs"
|
do
|
||||||
|
CASE("pairs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local C1 = world:component()
|
local C1 = world:component()
|
||||||
|
@ -266,7 +271,8 @@ TEST("world:set()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:remove()", function()
|
TEST("world:remove()", function()
|
||||||
do CASE "should allow remove a component that doesn't exist on entity"
|
do
|
||||||
|
CASE("should allow remove a component that doesn't exist on entity")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:component()
|
local Health = world:component()
|
||||||
|
@ -287,8 +293,8 @@ TEST("world:remove()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:add()", function()
|
TEST("world:add()", function()
|
||||||
|
do
|
||||||
do CASE "idempotent"
|
CASE("idempotent")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local d = debug_world_inspect(world)
|
local d = debug_world_inspect(world)
|
||||||
local _1, _2 = world:component(), world:component()
|
local _1, _2 = world:component(), world:component()
|
||||||
|
@ -300,7 +306,8 @@ TEST("world:add()", function()
|
||||||
CHECK(d.archetype(e) == "1_2")
|
CHECK(d.archetype(e) == "1_2")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "archetype move"
|
do
|
||||||
|
CASE("archetype move")
|
||||||
do
|
do
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
|
@ -325,7 +332,8 @@ TEST("world:add()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:query()", function()
|
TEST("world:query()", function()
|
||||||
do CASE "multiple iter"
|
do
|
||||||
|
CASE("multiple iter")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -342,7 +350,8 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
CHECK(counter == 2)
|
CHECK(counter == 2)
|
||||||
end
|
end
|
||||||
do CASE "tag"
|
do
|
||||||
|
CASE("tag")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:entity()
|
local A = world:entity()
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
|
@ -351,7 +360,8 @@ TEST("world:query()", function()
|
||||||
CHECK(a == nil)
|
CHECK(a == nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
do CASE "pairs"
|
do
|
||||||
|
CASE("pairs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local C1 = world:component()
|
local C1 = world:component()
|
||||||
|
@ -373,7 +383,8 @@ TEST("world:query()", function()
|
||||||
CHECK(d == nil)
|
CHECK(d == nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
do CASE "query single component"
|
do
|
||||||
|
CASE("query single component")
|
||||||
do
|
do
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -415,17 +426,18 @@ TEST("world:query()", function()
|
||||||
local i = 0
|
local i = 0
|
||||||
local j = 0
|
local j = 0
|
||||||
for _ in q do
|
for _ in q do
|
||||||
i+=1
|
i += 1
|
||||||
end
|
end
|
||||||
for _ in q do
|
for _ in q do
|
||||||
j+=1
|
j += 1
|
||||||
end
|
end
|
||||||
CHECK(i == 2)
|
CHECK(i == 2)
|
||||||
CHECK(j == 0)
|
CHECK(j == 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "query missing component"
|
do
|
||||||
|
CASE("query missing component")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -446,7 +458,8 @@ TEST("world:query()", function()
|
||||||
CHECK(counter == 0)
|
CHECK(counter == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "query more than 8 components"
|
do
|
||||||
|
CASE("query more than 8 components")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local components = {}
|
local components = {}
|
||||||
|
|
||||||
|
@ -456,23 +469,24 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
for i, id in components do
|
for i, id in components do
|
||||||
world:set(e, id, 13^i)
|
world:set(e, id, 13 ^ i)
|
||||||
end
|
end
|
||||||
|
|
||||||
for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) do
|
for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) do
|
||||||
CHECK(a == 13^1)
|
CHECK(a == 13 ^ 1)
|
||||||
CHECK(b == 13^2)
|
CHECK(b == 13 ^ 2)
|
||||||
CHECK(c == 13^3)
|
CHECK(c == 13 ^ 3)
|
||||||
CHECK(d == 13^4)
|
CHECK(d == 13 ^ 4)
|
||||||
CHECK(e == 13^5)
|
CHECK(e == 13 ^ 5)
|
||||||
CHECK(f == 13^6)
|
CHECK(f == 13 ^ 6)
|
||||||
CHECK(g == 13^7)
|
CHECK(g == 13 ^ 7)
|
||||||
CHECK(h == 13^8)
|
CHECK(h == 13 ^ 8)
|
||||||
CHECK(i == 13^9)
|
CHECK(i == 13 ^ 9)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should be able to get next results"
|
do
|
||||||
|
CASE("should be able to get next results")
|
||||||
local world = jecs.World.new() :: World
|
local world = jecs.World.new() :: World
|
||||||
world:component()
|
world:component()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -502,7 +516,8 @@ TEST("world:query()", function()
|
||||||
CHECK(true)
|
CHECK(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should query all matching entities when irrelevant component is removed")
|
do
|
||||||
|
CASE("should query all matching entities when irrelevant component is removed")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -531,7 +546,8 @@ TEST("world:query()", function()
|
||||||
CHECK(added == N)
|
CHECK(added == N)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should query all entities without B")
|
do
|
||||||
|
CASE("should query all entities without B")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -555,7 +571,8 @@ TEST("world:query()", function()
|
||||||
CHECK(#entities == 0)
|
CHECK(#entities == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should allow querying for relations")
|
do
|
||||||
|
CASE("should allow querying for relations")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:component()
|
local Eats = world:component()
|
||||||
local Apples = world:component()
|
local Apples = world:component()
|
||||||
|
@ -568,7 +585,8 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should allow wildcards in queries")
|
do
|
||||||
|
CASE("should allow wildcards in queries")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:component()
|
local Eats = world:component()
|
||||||
local Apples = world:entity()
|
local Apples = world:entity()
|
||||||
|
@ -587,7 +605,8 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should match against multiple pairs")
|
do
|
||||||
|
CASE("should match against multiple pairs")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:component()
|
local Eats = world:component()
|
||||||
local Apples = world:entity()
|
local Apples = world:entity()
|
||||||
|
@ -619,7 +638,8 @@ TEST("world:query()", function()
|
||||||
CHECK(count == 1)
|
CHECK(count == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should only relate alive entities"
|
do
|
||||||
|
CASE("should only relate alive entities")
|
||||||
SKIP()
|
SKIP()
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:entity()
|
local Eats = world:entity()
|
||||||
|
@ -646,7 +666,8 @@ TEST("world:query()", function()
|
||||||
CHECK(world:get(bob, pair(Eats, Apples)) == nil)
|
CHECK(world:get(bob, pair(Eats, Apples)) == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should error when setting invalid pair")
|
do
|
||||||
|
CASE("should error when setting invalid pair")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Eats = world:component()
|
local Eats = world:component()
|
||||||
local Apples = world:component()
|
local Apples = world:component()
|
||||||
|
@ -657,7 +678,8 @@ TEST("world:query()", function()
|
||||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE("should find target for ChildOf")
|
do
|
||||||
|
CASE("should find target for ChildOf")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local ChildOf = jecs.ChildOf
|
local ChildOf = jecs.ChildOf
|
||||||
|
|
||||||
|
@ -680,7 +702,8 @@ TEST("world:query()", function()
|
||||||
CHECK(count == 2)
|
CHECK(count == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "despawning while iterating"
|
do
|
||||||
|
CASE("despawning while iterating")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -699,8 +722,10 @@ TEST("world:query()", function()
|
||||||
CHECK(count == 2)
|
CHECK(count == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "iterator invalidation"
|
do
|
||||||
do CASE "adding"
|
CASE("iterator invalidation")
|
||||||
|
do
|
||||||
|
CASE("adding")
|
||||||
SKIP()
|
SKIP()
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -722,7 +747,8 @@ TEST("world:query()", function()
|
||||||
CHECK(count == 2)
|
CHECK(count == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "spawning"
|
do
|
||||||
|
CASE("spawning")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -743,7 +769,8 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should not find any entities"
|
do
|
||||||
|
CASE("should not find any entities")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Hello = world:component()
|
local Hello = world:component()
|
||||||
|
@ -761,7 +788,8 @@ TEST("world:query()", function()
|
||||||
CHECK(withoutCount == 0)
|
CHECK(withoutCount == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Empty Query"
|
do
|
||||||
|
CASE("Empty Query")
|
||||||
do
|
do
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -794,7 +822,8 @@ TEST("world:query()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "without"
|
do
|
||||||
|
CASE("without")
|
||||||
do
|
do
|
||||||
-- REGRESSION TEST
|
-- REGRESSION TEST
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -810,7 +839,8 @@ TEST("world:query()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:clear()", function()
|
TEST("world:clear()", function()
|
||||||
do CASE("should remove its components")
|
do
|
||||||
|
CASE("should remove its components")
|
||||||
local world = jecs.World.new() :: World
|
local world = jecs.World.new() :: World
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -828,7 +858,8 @@ TEST("world:clear()", function()
|
||||||
CHECK(world:get(e, B) == nil)
|
CHECK(world:get(e, B) == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should move last record"
|
do
|
||||||
|
CASE("should move last record")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
|
||||||
|
@ -872,7 +903,8 @@ TEST("world:clear()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:has()", function()
|
TEST("world:has()", function()
|
||||||
do CASE "should find Tag on entity"
|
do
|
||||||
|
CASE("should find Tag on entity")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Tag = world:entity()
|
local Tag = world:entity()
|
||||||
|
@ -883,7 +915,8 @@ TEST("world:has()", function()
|
||||||
CHECK(world:has(e, Tag))
|
CHECK(world:has(e, Tag))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should return false when missing one tag"
|
do
|
||||||
|
CASE("should return false when missing one tag")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local A = world:entity()
|
local A = world:entity()
|
||||||
|
@ -901,7 +934,8 @@ TEST("world:has()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:component()", function()
|
TEST("world:component()", function()
|
||||||
do CASE "only components should have EcsComponent trait"
|
do
|
||||||
|
CASE("only components should have EcsComponent trait")
|
||||||
local world = jecs.World.new() :: World
|
local world = jecs.World.new() :: World
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
|
@ -910,7 +944,8 @@ TEST("world:component()", function()
|
||||||
CHECK(not world:has(e, jecs.Component))
|
CHECK(not world:has(e, jecs.Component))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "tag"
|
do
|
||||||
|
CASE("tag")
|
||||||
local world = jecs.World.new() :: World
|
local world = jecs.World.new() :: World
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:entity()
|
local B = world:entity()
|
||||||
|
@ -928,7 +963,8 @@ TEST("world:component()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:delete", function()
|
TEST("world:delete", function()
|
||||||
do CASE "bug: Empty entity does not respect cleanup policy"
|
do
|
||||||
|
CASE("bug: Empty entity does not respect cleanup policy")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
local parent = world:entity()
|
local parent = world:entity()
|
||||||
local tag = world:entity()
|
local tag = world:entity()
|
||||||
|
@ -947,7 +983,8 @@ TEST("world:delete", function()
|
||||||
CHECK(not world:contains(tag))
|
CHECK(not world:contains(tag))
|
||||||
CHECK(not world:has(entity, tag)) -- => true
|
CHECK(not world:has(entity, tag)) -- => true
|
||||||
end
|
end
|
||||||
do CASE("should allow deleting components")
|
do
|
||||||
|
CASE("should allow deleting components")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:component()
|
local Health = world:component()
|
||||||
|
@ -969,7 +1006,8 @@ TEST("world:delete", function()
|
||||||
CHECK(world:get(id1, Health) == 50)
|
CHECK(world:get(id1, Health) == 50)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "delete entities using another Entity as component with Delete cleanup action"
|
do
|
||||||
|
CASE("delete entities using another Entity as component with Delete cleanup action")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:entity()
|
local Health = world:entity()
|
||||||
|
@ -998,7 +1036,8 @@ TEST("world:delete", function()
|
||||||
CHECK(not world:has(id1, Health))
|
CHECK(not world:has(id1, Health))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "delete children"
|
do
|
||||||
|
CASE("delete children")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:component()
|
local Health = world:component()
|
||||||
|
@ -1018,7 +1057,6 @@ TEST("world:delete", function()
|
||||||
table.insert(children, child)
|
table.insert(children, child)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
BENCH("delete children of entity", function()
|
BENCH("delete children of entity", function()
|
||||||
world:delete(e)
|
world:delete(e)
|
||||||
end)
|
end)
|
||||||
|
@ -1050,7 +1088,8 @@ TEST("world:delete", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "fast delete"
|
do
|
||||||
|
CASE("fast delete")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local entities = {}
|
local entities = {}
|
||||||
|
@ -1076,7 +1115,8 @@ TEST("world:delete", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "cycle"
|
do
|
||||||
|
CASE("cycle")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Likes = world:component()
|
local Likes = world:component()
|
||||||
world:add(Likes, pair(jecs.OnDeleteTarget, jecs.Delete))
|
world:add(Likes, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
|
@ -1093,7 +1133,8 @@ TEST("world:delete", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:target", function()
|
TEST("world:target", function()
|
||||||
do CASE "nth index"
|
do
|
||||||
|
CASE("nth index")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -1117,7 +1158,8 @@ TEST("world:target", function()
|
||||||
CHECK(world:target(e, C, 1) == nil)
|
CHECK(world:target(e, C, 1) == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "infer index when unspecified"
|
do
|
||||||
|
CASE("infer index when unspecified")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
@ -1136,7 +1178,8 @@ TEST("world:target", function()
|
||||||
CHECK(world:target(e, C) == world:target(e, C, 0))
|
CHECK(world:target(e, C) == world:target(e, C, 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "loop until no target"
|
do
|
||||||
|
CASE("loop until no target")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
|
|
||||||
local ROOT = world:entity()
|
local ROOT = world:entity()
|
||||||
|
@ -1152,14 +1195,13 @@ TEST("world:target", function()
|
||||||
local i = 0
|
local i = 0
|
||||||
local target = world:target(e1, ROOT, 0)
|
local target = world:target(e1, ROOT, 0)
|
||||||
while target do
|
while target do
|
||||||
i+=1
|
i += 1
|
||||||
CHECK(targets[i] == target)
|
CHECK(targets[i] == target)
|
||||||
target = world:target(e1, ROOT, i)
|
target = world:target(e1, ROOT, i)
|
||||||
end
|
end
|
||||||
|
|
||||||
CHECK(i == 10)
|
CHECK(i == 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:contains", function()
|
TEST("world:contains", function()
|
||||||
|
@ -1167,16 +1209,23 @@ TEST("world:contains", function()
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
CHECK(world:contains(id))
|
CHECK(world:contains(id))
|
||||||
|
|
||||||
do CASE "should not exist after delete"
|
do
|
||||||
|
CASE("should not exist after delete")
|
||||||
world:delete(id)
|
world:delete(id)
|
||||||
CHECK(not world:contains(id))
|
CHECK(not world:contains(id))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
type Tracker<T> = { track: (world: World, fn: (changes: {
|
type Tracker<T> = {
|
||||||
|
track: (
|
||||||
|
world: World,
|
||||||
|
fn: (
|
||||||
|
changes: {
|
||||||
added: () -> () -> (number, T),
|
added: () -> () -> (number, T),
|
||||||
removed: () -> () -> number,
|
removed: () -> () -> number,
|
||||||
changed: () -> () -> (number, T, T)
|
changed: () -> () -> (number, T, T),
|
||||||
}) -> ()) -> ()
|
}
|
||||||
|
) -> ()
|
||||||
|
) -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Entity<T = any> = number & { __nominal_type_dont_use: T }
|
type Entity<T = any> = number & { __nominal_type_dont_use: T }
|
||||||
|
@ -1298,7 +1347,8 @@ end
|
||||||
TEST("changetracker:track()", function()
|
TEST("changetracker:track()", function()
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
do CASE "added"
|
do
|
||||||
|
CASE("added")
|
||||||
local Test = world:component() :: Entity<{ foo: number }>
|
local Test = world:component() :: Entity<{ foo: number }>
|
||||||
local TestTracker = ChangeTracker(world, Test)
|
local TestTracker = ChangeTracker(world, Test)
|
||||||
|
|
||||||
|
@ -1309,7 +1359,7 @@ TEST("changetracker:track()", function()
|
||||||
TestTracker.track(function(changes)
|
TestTracker.track(function(changes)
|
||||||
local added = 0
|
local added = 0
|
||||||
for e, test in changes.added() do
|
for e, test in changes.added() do
|
||||||
added+=1
|
added += 1
|
||||||
CHECK(test == data)
|
CHECK(test == data)
|
||||||
end
|
end
|
||||||
for e, old, new in changes.changed() do
|
for e, old, new in changes.changed() do
|
||||||
|
@ -1321,7 +1371,8 @@ TEST("changetracker:track()", function()
|
||||||
CHECK(added == 1)
|
CHECK(added == 1)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
do CASE "changed"
|
do
|
||||||
|
CASE("changed")
|
||||||
local Test = world:component() :: Entity<{ foo: number }>
|
local Test = world:component() :: Entity<{ foo: number }>
|
||||||
local TestTracker = ChangeTracker(world, Test)
|
local TestTracker = ChangeTracker(world, Test)
|
||||||
|
|
||||||
|
@ -1329,8 +1380,7 @@ TEST("changetracker:track()", function()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
world:set(e1, Test, data)
|
world:set(e1, Test, data)
|
||||||
|
|
||||||
TestTracker.track(function(changes)
|
TestTracker.track(function(changes) end)
|
||||||
end)
|
|
||||||
|
|
||||||
data.foo += 1
|
data.foo += 1
|
||||||
|
|
||||||
|
@ -1344,12 +1394,13 @@ TEST("changetracker:track()", function()
|
||||||
CHECK(new == data)
|
CHECK(new == data)
|
||||||
CHECK(old ~= new)
|
CHECK(old ~= new)
|
||||||
CHECK(diff(new, old))
|
CHECK(diff(new, old))
|
||||||
changed +=1
|
changed += 1
|
||||||
end
|
end
|
||||||
CHECK(changed == 1)
|
CHECK(changed == 1)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
do CASE "removed"
|
do
|
||||||
|
CASE("removed")
|
||||||
local Test = world:component() :: Entity<{ foo: number }>
|
local Test = world:component() :: Entity<{ foo: number }>
|
||||||
local TestTracker = ChangeTracker(world, Test)
|
local TestTracker = ChangeTracker(world, Test)
|
||||||
|
|
||||||
|
@ -1357,8 +1408,7 @@ TEST("changetracker:track()", function()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
world:set(e1, Test, data)
|
world:set(e1, Test, data)
|
||||||
|
|
||||||
TestTracker.track(function(changes)
|
TestTracker.track(function(changes) end)
|
||||||
end)
|
|
||||||
|
|
||||||
world:remove(e1, Test)
|
world:remove(e1, Test)
|
||||||
|
|
||||||
|
@ -1378,7 +1428,8 @@ TEST("changetracker:track()", function()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "multiple change trackers"
|
do
|
||||||
|
CASE("multiple change trackers")
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
local trackerA = ChangeTracker(world, A)
|
local trackerA = ChangeTracker(world, A)
|
||||||
|
@ -1402,9 +1453,7 @@ TEST("changetracker:track()", function()
|
||||||
CHECK(new == "b2")
|
CHECK(new == "b2")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function create_cache(hook)
|
local function create_cache(hook)
|
||||||
|
@ -1413,7 +1462,7 @@ local function create_cache(hook)
|
||||||
local column = {}
|
local column = {}
|
||||||
self[component] = column
|
self[component] = column
|
||||||
return column
|
return column
|
||||||
end
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return function(world, component, fn)
|
return function(world, component, fn)
|
||||||
|
@ -1430,11 +1479,12 @@ end
|
||||||
local hooks = {
|
local hooks = {
|
||||||
OnSet = create_cache(jecs.OnSet),
|
OnSet = create_cache(jecs.OnSet),
|
||||||
OnAdd = create_cache(jecs.OnAdd),
|
OnAdd = create_cache(jecs.OnAdd),
|
||||||
OnRemove = create_cache(jecs.OnRemove)
|
OnRemove = create_cache(jecs.OnRemove),
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST("Hooks", function()
|
TEST("Hooks", function()
|
||||||
do CASE "OnAdd"
|
do
|
||||||
|
CASE("OnAdd")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Transform = world:component()
|
local Transform = world:component()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
|
@ -1444,7 +1494,8 @@ TEST("Hooks", function()
|
||||||
world:add(e1, Transform)
|
world:add(e1, Transform)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "OnSet"
|
do
|
||||||
|
CASE("OnSet")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Number = world:component()
|
local Number = world:component()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
|
@ -1462,7 +1513,8 @@ TEST("Hooks", function()
|
||||||
world:set(e1, Number, 1)
|
world:set(e1, Number, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "OnRemove"
|
do
|
||||||
|
CASE("OnRemove")
|
||||||
do
|
do
|
||||||
-- basic
|
-- basic
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -1496,7 +1548,8 @@ TEST("Hooks", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "the filip incident"
|
do
|
||||||
|
CASE("the filip incident")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
export type Iterator<T> = () -> (Entity, T?, T?)
|
export type Iterator<T> = () -> (Entity, T?, T?)
|
||||||
|
@ -1588,52 +1641,52 @@ TEST("Hooks", function()
|
||||||
local iter, destroy = ChangeTracker(Transform)
|
local iter, destroy = ChangeTracker(Transform)
|
||||||
|
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
world:set(e1, Transform, {1,1})
|
world:set(e1, Transform, { 1, 1 })
|
||||||
local counter = 0
|
local counter = 0
|
||||||
for _ in iter do
|
for _ in iter do
|
||||||
counter += 1
|
counter += 1
|
||||||
end
|
end
|
||||||
CHECK(counter == 1)
|
CHECK(counter == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("scheduler", function()
|
TEST("scheduler", function()
|
||||||
type System = {
|
type System = {
|
||||||
callback: (world: World) -> ()
|
callback: (world: World) -> (),
|
||||||
}
|
}
|
||||||
type Systems = { System }
|
type Systems = { System }
|
||||||
|
|
||||||
|
|
||||||
type Events = {
|
type Events = {
|
||||||
RenderStepped: Systems,
|
RenderStepped: Systems,
|
||||||
Heartbeat: Systems
|
Heartbeat: Systems,
|
||||||
}
|
}
|
||||||
|
|
||||||
local scheduler_new: (w: World) -> {
|
local scheduler_new: (
|
||||||
|
w: World
|
||||||
|
) -> {
|
||||||
components: {
|
components: {
|
||||||
Disabled: Entity,
|
Disabled: Entity,
|
||||||
System: Entity<System>,
|
System: Entity<System>,
|
||||||
Phase: Entity,
|
Phase: Entity,
|
||||||
DependsOn: Entity
|
DependsOn: Entity,
|
||||||
},
|
},
|
||||||
|
|
||||||
collect: {
|
collect: {
|
||||||
under_event: (event: Entity) -> Systems,
|
under_event: (event: Entity) -> Systems,
|
||||||
all: () -> Events
|
all: () -> Events,
|
||||||
},
|
},
|
||||||
|
|
||||||
systems: {
|
systems: {
|
||||||
run: (events: Events) -> (),
|
run: (events: Events) -> (),
|
||||||
new: (callback: (world: World) -> (), phase: Entity) -> Entity
|
new: (callback: (world: World) -> (), phase: Entity) -> Entity,
|
||||||
},
|
},
|
||||||
|
|
||||||
phases: {
|
phases: {
|
||||||
RenderStepped: Entity,
|
RenderStepped: Entity,
|
||||||
Heartbeat: Entity
|
Heartbeat: Entity,
|
||||||
},
|
},
|
||||||
|
|
||||||
phase: (after: Entity) -> Entity
|
phase: (after: Entity) -> Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
|
@ -1731,18 +1784,19 @@ TEST("scheduler", function()
|
||||||
|
|
||||||
collect = {
|
collect = {
|
||||||
under_event = scheduler_collect_systems_under_event,
|
under_event = scheduler_collect_systems_under_event,
|
||||||
all = scheduler_collect_systems_all
|
all = scheduler_collect_systems_all,
|
||||||
},
|
},
|
||||||
|
|
||||||
systems = {
|
systems = {
|
||||||
new = scheduler_systems_new,
|
new = scheduler_systems_new,
|
||||||
run = scheduler_systems_run
|
run = scheduler_systems_run,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "event dependant phase"
|
do
|
||||||
|
CASE("event dependant phase")
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local scheduler = scheduler_new(world)
|
local scheduler = scheduler_new(world)
|
||||||
|
@ -1755,7 +1809,8 @@ TEST("scheduler", function()
|
||||||
CHECK(world:target(Physics, DependsOn, 0) == Heartbeat)
|
CHECK(world:target(Physics, DependsOn, 0) == Heartbeat)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "user-defined sub phases"
|
do
|
||||||
|
CASE("user-defined sub phases")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local scheduler = scheduler_new(world)
|
local scheduler = scheduler_new(world)
|
||||||
local components = scheduler.components
|
local components = scheduler.components
|
||||||
|
@ -1768,7 +1823,8 @@ TEST("scheduler", function()
|
||||||
CHECK(world:target(B, DependsOn, 0) == A)
|
CHECK(world:target(B, DependsOn, 0) == A)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "phase order"
|
do
|
||||||
|
CASE("phase order")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local scheduler = scheduler_new(world)
|
local scheduler = scheduler_new(world)
|
||||||
|
|
||||||
|
@ -1799,7 +1855,8 @@ TEST("scheduler", function()
|
||||||
CHECK(order == "BEGIN->move->hit->END")
|
CHECK(order == "BEGIN->move->hit->END")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "collect only systems under phase recursive"
|
do
|
||||||
|
CASE("collect only systems under phase recursive")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local scheduler = scheduler_new(world)
|
local scheduler = scheduler_new(world)
|
||||||
local phases = scheduler.phases
|
local phases = scheduler.phases
|
||||||
|
@ -1809,14 +1866,11 @@ TEST("scheduler", function()
|
||||||
local Physics = scheduler.phase(Heartbeat)
|
local Physics = scheduler.phase(Heartbeat)
|
||||||
local Collisions = scheduler.phase(Physics)
|
local Collisions = scheduler.phase(Physics)
|
||||||
|
|
||||||
local function move()
|
local function move() end
|
||||||
end
|
|
||||||
|
|
||||||
local function hit()
|
local function hit() end
|
||||||
end
|
|
||||||
|
|
||||||
local function camera()
|
local function camera() end
|
||||||
end
|
|
||||||
|
|
||||||
local createSystem = scheduler.systems.new
|
local createSystem = scheduler.systems.new
|
||||||
|
|
||||||
|
@ -1845,7 +1899,8 @@ TEST("scheduler", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("repro", function()
|
TEST("repro", function()
|
||||||
do CASE ""
|
do
|
||||||
|
CASE("")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
local reproEntity = world:component()
|
local reproEntity = world:component()
|
||||||
local components = { Cooldown = world:component() }
|
local components = { Cooldown = world:component() }
|
||||||
|
@ -1859,7 +1914,7 @@ TEST("repro", function()
|
||||||
|
|
||||||
if cooldown <= 0 then
|
if cooldown <= 0 then
|
||||||
table.insert(toRemove, id)
|
table.insert(toRemove, id)
|
||||||
print('removing')
|
print("removing")
|
||||||
-- world:remove(id, components.Cooldown)
|
-- world:remove(id, components.Cooldown)
|
||||||
else
|
else
|
||||||
world:set(id, components.Cooldown, cooldown)
|
world:set(id, components.Cooldown, cooldown)
|
||||||
|
|
96
testkit.luau
96
testkit.luau
|
@ -96,7 +96,7 @@ local function convert_units(unit: string, value: number): (number, string)
|
||||||
return value * sign, prefix_colors[order](prefixes[order] .. unit)
|
return value * sign, prefix_colors[order](prefixes[order] .. unit)
|
||||||
end
|
end
|
||||||
|
|
||||||
local WALL = color.gray "│"
|
local WALL = color.gray("│")
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Testing
|
-- Testing
|
||||||
|
@ -118,7 +118,7 @@ type Case = {
|
||||||
name: string,
|
name: string,
|
||||||
result: number,
|
result: number,
|
||||||
line: number?,
|
line: number?,
|
||||||
focus: boolean
|
focus: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
local PASS, FAIL, NONE, ERROR, SKIPPED = 1, 2, 3, 4, 5
|
local PASS, FAIL, NONE, ERROR, SKIPPED = 1, 2, 3, 4, 5
|
||||||
|
@ -129,35 +129,38 @@ local test: Test?
|
||||||
local tests: { Test } = {}
|
local tests: { Test } = {}
|
||||||
|
|
||||||
local function output_test_result(test: Test)
|
local function output_test_result(test: Test)
|
||||||
|
|
||||||
if check_for_focused then
|
if check_for_focused then
|
||||||
local any_focused = test.focus
|
local any_focused = test.focus
|
||||||
for _, case in test.cases do
|
for _, case in test.cases do
|
||||||
any_focused = any_focused or case.focus
|
any_focused = any_focused or case.focus
|
||||||
end
|
end
|
||||||
|
|
||||||
if not any_focused then return end
|
if not any_focused then
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print(color.white(test.name))
|
print(color.white(test.name))
|
||||||
|
|
||||||
for _, case in test.cases do
|
for _, case in test.cases do
|
||||||
local status = ({
|
local status = ({
|
||||||
[PASS] = color.green "PASS",
|
[PASS] = color.green("PASS"),
|
||||||
[FAIL] = color.red "FAIL",
|
[FAIL] = color.red("FAIL"),
|
||||||
[NONE] = color.orange "NONE",
|
[NONE] = color.orange("NONE"),
|
||||||
[ERROR] = color.red "FAIL",
|
[ERROR] = color.red("FAIL"),
|
||||||
[SKIPPED] = color.yellow "SKIP"
|
[SKIPPED] = color.yellow("SKIP"),
|
||||||
})[case.result]
|
})[case.result]
|
||||||
|
|
||||||
local line = case.result == FAIL and color.red(`{case.line}:`) or ""
|
local line = case.result == FAIL and color.red(`{case.line}:`) or ""
|
||||||
if check_for_focused and case.focus == false and test.focus == false then continue end
|
if check_for_focused and case.focus == false and test.focus == false then
|
||||||
|
continue
|
||||||
|
end
|
||||||
print(`{status}{WALL} {line}{color.gray(case.name)}`)
|
print(`{status}{WALL} {line}{color.gray(case.name)}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
if test.error then
|
if test.error then
|
||||||
print(color.gray "error: " .. color.red(test.error.message))
|
print(color.gray("error: ") .. color.red(test.error.message))
|
||||||
print(color.gray "trace: " .. color.red(test.error.trace))
|
print(color.gray("trace: ") .. color.red(test.error.trace))
|
||||||
else
|
else
|
||||||
print()
|
print()
|
||||||
end
|
end
|
||||||
|
@ -170,7 +173,7 @@ local function CASE(name: string)
|
||||||
local case = {
|
local case = {
|
||||||
name = name,
|
name = name,
|
||||||
result = NONE,
|
result = NONE,
|
||||||
focus = false
|
focus = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
test.case = case
|
test.case = case
|
||||||
|
@ -183,7 +186,7 @@ local function CHECK<T>(value: T, stack: number?): T?
|
||||||
local case = test.case
|
local case = test.case
|
||||||
|
|
||||||
if not case then
|
if not case then
|
||||||
CASE ""
|
CASE("")
|
||||||
case = test.case
|
case = test.case
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -208,7 +211,7 @@ local function TEST(name: string, fn: () -> ())
|
||||||
name = name,
|
name = name,
|
||||||
cases = {},
|
cases = {},
|
||||||
duration = 0,
|
duration = 0,
|
||||||
focus = false
|
focus = false,
|
||||||
}
|
}
|
||||||
assert(test)
|
assert(test)
|
||||||
|
|
||||||
|
@ -221,7 +224,9 @@ local function TEST(name: string, fn: () -> ())
|
||||||
end)
|
end)
|
||||||
test.duration = os.clock() - start
|
test.duration = os.clock() - start
|
||||||
|
|
||||||
if not test.case then CASE "" end
|
if not test.case then
|
||||||
|
CASE("")
|
||||||
|
end
|
||||||
assert(test.case, "no active case")
|
assert(test.case, "no active case")
|
||||||
|
|
||||||
if not success then
|
if not success then
|
||||||
|
@ -271,27 +276,14 @@ local function FINISH(): boolean
|
||||||
output_test_result(test)
|
output_test_result(test)
|
||||||
end
|
end
|
||||||
|
|
||||||
print(
|
print(color.gray(string.format(`{passed_cases}/{total_cases} test cases passed in %.3f ms.`, duration * 1e3)))
|
||||||
color.gray(
|
|
||||||
string.format(
|
|
||||||
`{passed_cases}/{total_cases} test cases passed in %.3f ms.`,
|
|
||||||
duration * 1e3
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if check_for_focused then
|
if check_for_focused then
|
||||||
print(
|
print(color.gray(`{passed_focus_cases}/{total_focus_cases} focused test cases passed`))
|
||||||
color.gray(`{passed_focus_cases}/{total_focus_cases} focused test cases passed`)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local fails = total_cases - passed_cases
|
local fails = total_cases - passed_cases
|
||||||
|
|
||||||
print(
|
print((fails > 0 and color.red or color.green)(`{fails} {fails == 1 and "fail" or "fails"}`))
|
||||||
(fails > 0 and color.red or color.green)(
|
|
||||||
`{fails} {fails == 1 and "fail" or "fails"}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
check_for_focused = false
|
check_for_focused = false
|
||||||
return success, table.clear(tests)
|
return success, table.clear(tests)
|
||||||
|
@ -331,7 +323,7 @@ local function BENCH(name: string, fn: () -> ())
|
||||||
|
|
||||||
bench = {}
|
bench = {}
|
||||||
assert(bench);
|
assert(bench);
|
||||||
(collectgarbage :: any) "collect"
|
(collectgarbage :: any)("collect")
|
||||||
|
|
||||||
local mem_start = gcinfo()
|
local mem_start = gcinfo()
|
||||||
local time_start = os.clock()
|
local time_start = os.clock()
|
||||||
|
@ -345,7 +337,7 @@ local function BENCH(name: string, fn: () -> ())
|
||||||
local mem_stop = gcinfo()
|
local mem_stop = gcinfo()
|
||||||
|
|
||||||
if not success then
|
if not success then
|
||||||
print(`{WALL}{color.red "ERROR"}{WALL} {name}`)
|
print(`{WALL}{color.red("ERROR")}{WALL} {name}`)
|
||||||
print(color.gray(err_msg :: string))
|
print(color.gray(err_msg :: string))
|
||||||
else
|
else
|
||||||
time_start = bench.time_start or time_start
|
time_start = bench.time_start or time_start
|
||||||
|
@ -353,14 +345,10 @@ local function BENCH(name: string, fn: () -> ())
|
||||||
|
|
||||||
local n = bench.iterations or 1
|
local n = bench.iterations or 1
|
||||||
local d, d_unit = convert_units("s", (time_stop - time_start) / n)
|
local d, d_unit = convert_units("s", (time_stop - time_start) / n)
|
||||||
local a, a_unit =
|
local a, a_unit = convert_units("B", math.round((mem_stop - mem_start) / n * 1e3))
|
||||||
convert_units("B", math.round((mem_stop - mem_start) / n * 1e3))
|
|
||||||
|
|
||||||
local function round(x: number): string
|
local function round(x: number): string
|
||||||
return x > 0
|
return x > 0 and x < 10 and (x - math.floor(x)) > 0 and string.format("%2.1f", x)
|
||||||
and x < 10
|
|
||||||
and (x - math.floor(x)) > 0
|
|
||||||
and string.format("%2.1f", x)
|
|
||||||
or string.format("%3.f", x)
|
or string.format("%3.f", x)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -394,9 +382,9 @@ local function print2(v: unknown)
|
||||||
|
|
||||||
if type(value) == "string" then
|
if type(value) == "string" then
|
||||||
local n = str.n
|
local n = str.n
|
||||||
str[n + 1] = '"'
|
str[n + 1] = "\""
|
||||||
str[n + 2] = value
|
str[n + 2] = value
|
||||||
str[n + 3] = '"'
|
str[n + 3] = "\""
|
||||||
str.n = n + 3
|
str.n = n + 3
|
||||||
elseif type(value) ~= "table" then
|
elseif type(value) ~= "table" then
|
||||||
local n = str.n
|
local n = str.n
|
||||||
|
@ -468,25 +456,35 @@ end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function shallow_eq(a: {}, b: {}): boolean
|
local function shallow_eq(a: {}, b: {}): boolean
|
||||||
if #a ~= #b then return false end
|
if #a ~= #b then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
for i, v in next, a do
|
for i, v in next, a do
|
||||||
if b[i] ~= v then return false end
|
if b[i] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i, v in next, b do
|
for i, v in next, b do
|
||||||
if a[i] ~= v then return false end
|
if a[i] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function deep_eq(a: {}, b: {}): boolean
|
local function deep_eq(a: {}, b: {}): boolean
|
||||||
if #a ~= #b then return false end
|
if #a ~= #b then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
for i, v in next, a do
|
for i, v in next, a do
|
||||||
if type(b[i]) == "table" and type(v) == "table" then
|
if type(b[i]) == "table" and type(v) == "table" then
|
||||||
if deep_eq(b[i], v) == false then return false end
|
if deep_eq(b[i], v) == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
elseif b[i] ~= v then
|
elseif b[i] ~= v then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -494,7 +492,9 @@ local function deep_eq(a: {}, b: {}): boolean
|
||||||
|
|
||||||
for i, v in next, b do
|
for i, v in next, b do
|
||||||
if type(a[i]) == "table" and type(v) == "table" then
|
if type(a[i]) == "table" and type(v) == "table" then
|
||||||
if deep_eq(a[i], v) == false then return false end
|
if deep_eq(a[i], v) == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
elseif a[i] ~= v then
|
elseif a[i] ~= v then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue