style: fix inconsistent project formatting

Closes #138
This commit is contained in:
christopher-buss 2024-10-12 20:28:41 +01:00
parent f82318c642
commit e81b572480
45 changed files with 2867 additions and 2855 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,
}; },
} }

View file

@ -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,
}, },

View file

@ -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)

View file

@ -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,
}; },
} }

View file

@ -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,
}, },
} }

View file

@ -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,
}, },
} }

View file

@ -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

View file

@ -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

View file

@ -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 }

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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,
} }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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,
}) })

View file

@ -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"

View file

@ -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?)

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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