fix: Bugs + temp push for bugfixing
This commit is contained in:
parent
88ca58df9b
commit
d34edf8d70
21 changed files with 2317 additions and 73 deletions
96
test/signal.luau
Normal file
96
test/signal.luau
Normal file
|
@ -0,0 +1,96 @@
|
|||
-- licensed under MIT
|
||||
-- @author jackdotink
|
||||
-- https://github.com/red-blox/Util/blob/main/libs/Signal/Signal.luau
|
||||
-- adapted to work in pure luau
|
||||
|
||||
type node<T...> = {
|
||||
next: node<T...>?,
|
||||
callback: (T...) -> (),
|
||||
}
|
||||
|
||||
export type signal<T...> = {
|
||||
root: node<T...>?,
|
||||
|
||||
connect: (self: signal<T...>, Callback: (T...) -> ()) -> () -> (),
|
||||
wait: (self: signal<T...>) -> T...,
|
||||
once: (self: signal<T...>, Callback: (T...) -> ()) -> () -> (),
|
||||
fire: (self: signal<T...>, T...) -> (),
|
||||
disconnect_all: (self: signal<T...>) -> (),
|
||||
}
|
||||
|
||||
local Signal = {}
|
||||
Signal.__index = Signal
|
||||
|
||||
-- Extracted this function from Connect as it results in the closure
|
||||
-- made in Connect using less memory because this function can be static
|
||||
local function disconnect<T...>(self: signal<T...>, Node: node<T...>)
|
||||
if self.root == Node then
|
||||
self.root = Node.next
|
||||
else
|
||||
local Current = self.root
|
||||
|
||||
while Current do
|
||||
if Current.next == Node then
|
||||
Current.next = Node.next
|
||||
break
|
||||
end
|
||||
|
||||
Current = Current.next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Signal.connect<T...>(self: signal<T...>, Callback: (T...) -> ()): () -> ()
|
||||
local node = {
|
||||
next = self.root,
|
||||
callback = Callback,
|
||||
}
|
||||
|
||||
self.root = node
|
||||
|
||||
return function()
|
||||
disconnect(self, node)
|
||||
end
|
||||
end
|
||||
|
||||
function Signal.wait<T...>(self: signal<T...>): T...
|
||||
local Thread = coroutine.running()
|
||||
local Disconnect
|
||||
|
||||
Disconnect = self:connect(function(...)
|
||||
Disconnect()
|
||||
coroutine.resume(Thread, ...)
|
||||
end)
|
||||
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
function Signal.once<T...>(self: signal<T...>, Callback: (T...) -> ()): () -> ()
|
||||
local Disconnect
|
||||
|
||||
Disconnect = self:connect(function(...)
|
||||
Disconnect()
|
||||
Callback(...)
|
||||
end)
|
||||
|
||||
return Disconnect
|
||||
end
|
||||
|
||||
function Signal.fire<T...>(self: signal<T...>, ...: T...)
|
||||
local Current = self.root
|
||||
|
||||
while Current do
|
||||
Current.callback(...)
|
||||
Current = Current.next
|
||||
end
|
||||
end
|
||||
|
||||
function Signal.disconnect_all<T...>(self: signal<T...>)
|
||||
self.root = nil
|
||||
end
|
||||
|
||||
return function<T...>(): signal<T...>
|
||||
return setmetatable({
|
||||
root = nil,
|
||||
}, Signal) :: any
|
||||
end
|
237
test/tests.luau
Normal file
237
test/tests.luau
Normal file
|
@ -0,0 +1,237 @@
|
|||
--!strict
|
||||
-- stylua: ignore start
|
||||
local jecs = require("@jecs")
|
||||
local jecs_utils = require("@jecs_utils")
|
||||
local testkit = require("@testkit")
|
||||
|
||||
local collect = jecs_utils.collect
|
||||
local handle = jecs_utils.handle
|
||||
local replicator = jecs_utils.replicator
|
||||
local ref = jecs_utils.ref
|
||||
local command_buffer = jecs_utils.command_buffer
|
||||
|
||||
local signal = require("./signal")
|
||||
|
||||
local BENCH, START = testkit.benchmark()
|
||||
|
||||
local TEST, CASE, CHECK, FINISH, SKIP, FOCUS = testkit.test()
|
||||
|
||||
TEST("jecs_utils.collect()", function()
|
||||
do CASE "collects"
|
||||
local sig: signal.signal<number> = signal()
|
||||
local flush = collect(sig)
|
||||
local should = {}
|
||||
|
||||
for idx = 100, 1, -1 do
|
||||
local n = math.random()
|
||||
should[idx] = n
|
||||
sig:fire(n)
|
||||
end
|
||||
|
||||
for idx, n in flush do
|
||||
CHECK(should[idx] == n)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
TEST("jecs_utils.handle()", function()
|
||||
do CASE "has"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
local tag = world:entity()
|
||||
|
||||
world:add(entity, tag)
|
||||
CHECK(handle(entity):has(tag))
|
||||
end
|
||||
|
||||
do CASE "get"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
local component = world:component()
|
||||
|
||||
world:set(entity, component, 50)
|
||||
CHECK(handle(entity):get(component) == 50)
|
||||
end
|
||||
|
||||
do CASE "add"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
local tag = world:entity()
|
||||
|
||||
handle(entity):add(tag)
|
||||
CHECK(world:has(entity, tag))
|
||||
end
|
||||
|
||||
do CASE "set"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
local component = world:component()
|
||||
|
||||
handle(entity):set(component, 50)
|
||||
CHECK(world:get(entity, component) == 50)
|
||||
end
|
||||
|
||||
do CASE "remove"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
local component = world:component()
|
||||
|
||||
handle(entity):set(component, 50)
|
||||
CHECK(world:get(entity, component) == 50)
|
||||
end
|
||||
|
||||
do CASE "delete"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
handle(entity):delete()
|
||||
CHECK(not world:contains(entity))
|
||||
end
|
||||
end)
|
||||
|
||||
TEST("jecs_utils.ref()", function()
|
||||
do CASE "ref(abc) == ref(abc)"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local a: number = ref(1234):id()
|
||||
local b: number = ref(1234):id()
|
||||
CHECK(a == b)
|
||||
end
|
||||
end)
|
||||
|
||||
TEST("jecs_utils.replicator()", function()
|
||||
do CASE "propagates difference"
|
||||
local world = jecs.World.new()
|
||||
local tag = world:entity()
|
||||
local component: jecs.Entity<number> = world:component()
|
||||
|
||||
local entity1 = world:entity()
|
||||
local entity2 = world:entity()
|
||||
|
||||
jecs_utils.initialize(world)
|
||||
local rep = replicator(component, tag)
|
||||
|
||||
world:add(entity1, tag)
|
||||
world:set(entity2, component, 50)
|
||||
|
||||
local difference: jecs_utils.changes = rep.calculate_difference() :: any
|
||||
CHECK(difference ~= nil)
|
||||
|
||||
local world2 = jecs.World.new()
|
||||
local component2: jecs.Entity<number> = world2:component()
|
||||
local tag2 = world2:entity()
|
||||
|
||||
jecs_utils.initialize(world2)
|
||||
local rep2 = replicator(component2, tag2)
|
||||
|
||||
rep2.apply_difference(difference)
|
||||
|
||||
CHECK(ref(`replicated-{entity1}`):has(tag2))
|
||||
CHECK(ref(`replicated-{entity2}`):get(component2) == 50)
|
||||
end
|
||||
|
||||
do CASE "propagates full data"
|
||||
local world = jecs.World.new()
|
||||
local tag = world:entity()
|
||||
local component: jecs.Entity<number> = world:component()
|
||||
|
||||
local entity1 = world:entity()
|
||||
local entity2 = world:entity()
|
||||
|
||||
jecs_utils.initialize(world)
|
||||
local rep = replicator(component, tag)
|
||||
|
||||
world:add(entity1, tag)
|
||||
world:set(entity2, component, 50)
|
||||
|
||||
local full_data = rep.get_full_data()
|
||||
CHECK(full_data ~= nil)
|
||||
|
||||
local world2 = jecs.World.new()
|
||||
local component2: jecs.Entity<number> = world2:component()
|
||||
local tag2 = world2:entity()
|
||||
|
||||
jecs_utils.initialize(world2)
|
||||
local rep2 = replicator(component2, tag2)
|
||||
|
||||
rep2.apply_difference(full_data)
|
||||
|
||||
CHECK(ref(`replicated-{entity1}`):has(tag2))
|
||||
CHECK(ref(`replicated-{entity2}`):get(component2) == 50)
|
||||
end
|
||||
end)
|
||||
|
||||
TEST("jecs_utils.command_buffer", function()
|
||||
do CASE "add"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local tag = world:entity()
|
||||
local entity = world:entity()
|
||||
command_buffer.add(entity, tag)
|
||||
|
||||
CHECK(not world:has(entity, tag))
|
||||
|
||||
command_buffer.flush()
|
||||
|
||||
CHECK(world:has(entity, tag))
|
||||
end
|
||||
|
||||
do CASE "set"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local component = world:component()
|
||||
local entity = world:entity()
|
||||
command_buffer.set(entity, component, 50)
|
||||
|
||||
CHECK(not world:has(entity, component))
|
||||
|
||||
command_buffer.flush()
|
||||
|
||||
CHECK(world:get(entity, component) == 50)
|
||||
end
|
||||
|
||||
do CASE "remove"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local component = world:component()
|
||||
local entity = world:entity()
|
||||
world:set(entity, component, 50)
|
||||
command_buffer.remove(entity, component)
|
||||
|
||||
CHECK(world:has(entity, component))
|
||||
|
||||
command_buffer.flush()
|
||||
|
||||
CHECK(not world:has(entity, component))
|
||||
end
|
||||
|
||||
do CASE "delete"
|
||||
local world = jecs.World.new()
|
||||
jecs_utils.initialize(world)
|
||||
|
||||
local entity = world:entity()
|
||||
command_buffer.delete(entity)
|
||||
|
||||
command_buffer.flush()
|
||||
|
||||
CHECK(not world:contains(entity))
|
||||
end
|
||||
end)
|
||||
|
||||
FINISH()
|
||||
-- stylua: ignore end
|
Loading…
Add table
Add a link
Reference in a new issue