fix: Refs and cmd buffers, add unit tests, v0.1.0
- Refs and command buffers used to share data with every single world, this is now mitigated by using a separate data set for every world - Working unit tests have been added - jecs-utils has now been released at mark-marks/jecs-utils@v0.1.0!
This commit is contained in:
parent
9a0aa37667
commit
5a043b2912
4 changed files with 92 additions and 52 deletions
|
@ -4,16 +4,27 @@ local jecs = require("./jecs")
|
||||||
type entity<T = nil> = jecs.Entity<T>
|
type entity<T = nil> = jecs.Entity<T>
|
||||||
type id<T = nil> = jecs.Id<T>
|
type id<T = nil> = jecs.Id<T>
|
||||||
|
|
||||||
local WORLD = require("./world").get
|
local _world = require("./world")
|
||||||
|
local WORLD = _world.get
|
||||||
|
|
||||||
|
-- luau-lsp literally dies if you use the actual world type
|
||||||
|
type jecs_world = any
|
||||||
|
|
||||||
--- `map<component_id, array<entity_id>>`
|
--- `map<component_id, array<entity_id>>`
|
||||||
local add_commands: { [id]: { entity } } = {}
|
local add_commands: { [jecs_world]: { [id]: { entity } } } = {}
|
||||||
--- `map<component_id, array<entity_id, component_value>>`
|
--- `map<component_id, array<entity_id, component_value>>`
|
||||||
local set_commands: { [id]: { [entity]: any } } = {}
|
local set_commands: { [jecs_world]: { [id]: { [entity]: any } } } = {}
|
||||||
--- `map<component_id, array<entity_id>>`
|
--- `map<component_id, array<entity_id>>`
|
||||||
local remove_commands: { [id]: { entity } } = {}
|
local remove_commands: { [jecs_world]: { [id]: { entity } } } = {}
|
||||||
--- `array<entity_id>`
|
--- `array<entity_id>`
|
||||||
local delete_commands: { entity } = {}
|
local delete_commands: { [jecs_world]: { entity } } = {}
|
||||||
|
|
||||||
|
_world.on_set(function(world)
|
||||||
|
add_commands[world] = {}
|
||||||
|
set_commands[world] = {}
|
||||||
|
remove_commands[world] = {}
|
||||||
|
delete_commands[world] = {}
|
||||||
|
end)
|
||||||
|
|
||||||
export type command_buffer = {
|
export type command_buffer = {
|
||||||
--- Execute all buffered commands and clear the buffer
|
--- Execute all buffered commands and clear the buffer
|
||||||
|
@ -30,74 +41,86 @@ export type command_buffer = {
|
||||||
}
|
}
|
||||||
|
|
||||||
local function flush()
|
local function flush()
|
||||||
local world = WORLD()
|
for world, entities in delete_commands do
|
||||||
|
|
||||||
for _, entity in delete_commands do
|
|
||||||
world:delete(entity)
|
|
||||||
end
|
|
||||||
|
|
||||||
for component, entities in add_commands do
|
|
||||||
for _, entity in entities do
|
for _, entity in entities do
|
||||||
if delete_commands[entity] then
|
world:delete(entity)
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
world:add(entity, component)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.clear(add_commands)
|
|
||||||
|
|
||||||
for component, entities in set_commands do
|
for world, commands in add_commands do
|
||||||
for entity, value in entities do
|
for component, entities in commands do
|
||||||
if delete_commands[entity] then
|
for _, entity in entities do
|
||||||
continue
|
if delete_commands[world][entity] then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
world:add(entity, component)
|
||||||
end
|
end
|
||||||
|
|
||||||
world:set(entity, component, value)
|
|
||||||
end
|
end
|
||||||
|
table.clear(add_commands[world])
|
||||||
end
|
end
|
||||||
table.clear(set_commands)
|
|
||||||
|
|
||||||
for component, entities in remove_commands do
|
for world, commands in set_commands do
|
||||||
for _, entity in entities do
|
for component, entities in commands do
|
||||||
if delete_commands[entity] then
|
for entity, value in entities do
|
||||||
continue
|
if delete_commands[world][entity] then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
world:set(entity, component, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
world:remove(entity, component)
|
|
||||||
end
|
end
|
||||||
|
table.clear(set_commands[world])
|
||||||
end
|
end
|
||||||
table.clear(remove_commands)
|
|
||||||
|
|
||||||
table.clear(delete_commands)
|
for world, commands in remove_commands do
|
||||||
|
for component, entities in commands do
|
||||||
|
for _, entity in entities do
|
||||||
|
if delete_commands[world][entity] then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
world:remove(entity, component)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.clear(remove_commands[world])
|
||||||
|
end
|
||||||
|
|
||||||
|
for world in delete_commands do
|
||||||
|
table.clear(delete_commands)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function add(entity: entity, component: id)
|
local function add(entity: entity, component: id)
|
||||||
if not add_commands[component] then
|
local world = WORLD()
|
||||||
add_commands[component] = {}
|
if not add_commands[world][component] then
|
||||||
|
add_commands[world][component] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(add_commands[component], entity)
|
table.insert(add_commands[world][component], entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set<T>(entity: entity, component: id<T>, data: T)
|
local function set<T>(entity: entity, component: id<T>, data: T)
|
||||||
if not set_commands[component] then
|
local world = WORLD()
|
||||||
set_commands[component] = {}
|
if not set_commands[world][component] then
|
||||||
|
set_commands[world][component] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
set_commands[component][entity] = data
|
set_commands[world][component][entity] = data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove(entity: entity, component: id)
|
local function remove(entity: entity, component: id)
|
||||||
if not remove_commands[component] then
|
local world = WORLD()
|
||||||
remove_commands[component] = {}
|
if not remove_commands[world][component] then
|
||||||
|
remove_commands[world][component] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(remove_commands[component], entity)
|
table.insert(remove_commands[world][component], entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function delete(entity: entity)
|
local function delete(entity: entity)
|
||||||
table.insert(delete_commands, entity)
|
local world = WORLD()
|
||||||
|
table.insert(delete_commands[world], entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local command_buffer: command_buffer = {
|
local command_buffer: command_buffer = {
|
||||||
|
|
|
@ -30,7 +30,7 @@ type interface = {
|
||||||
id: (self: handle) -> entity,
|
id: (self: handle) -> entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type handle = typeof(setmetatable({} :: { entity: entity }, {} :: interface))
|
export type handle = typeof(setmetatable({} :: { entity: entity, world: jecs.World }, {} :: interface))
|
||||||
|
|
||||||
local handle = {} :: interface
|
local handle = {} :: interface
|
||||||
handle.__index = handle
|
handle.__index = handle
|
||||||
|
@ -38,26 +38,27 @@ handle.__index = handle
|
||||||
function handle.new(entity: entity)
|
function handle.new(entity: entity)
|
||||||
local self = {
|
local self = {
|
||||||
entity = entity,
|
entity = entity,
|
||||||
|
world = world(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return setmetatable(self, handle)
|
return setmetatable(self, handle)
|
||||||
end
|
end
|
||||||
|
|
||||||
function handle:has(...: id): boolean
|
function handle:has(...: id): boolean
|
||||||
return world():has(self.entity, ...)
|
return self.world:has(self.entity, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
handle.get = function(self: handle, a: id, b: id?, c: id?, d: id?)
|
handle.get = function(self: handle, a: id, b: id?, c: id?, d: id?)
|
||||||
return world():get(self.entity, a, b :: any, c :: any, d :: any)
|
return self.world:get(self.entity, a, b :: any, c :: any, d :: any)
|
||||||
end :: any
|
end :: any
|
||||||
|
|
||||||
function handle:add<T>(id: id<T>): handle
|
function handle:add<T>(id: id<T>): handle
|
||||||
world():add(self.entity, id)
|
self.world:add(self.entity, id)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function handle:set<T>(id: id<T>, value: T): handle
|
function handle:set<T>(id: id<T>, value: T): handle
|
||||||
world():set(self.entity, id, value)
|
self.world:set(self.entity, id, value)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ function handle:remove(id: id): handle
|
||||||
end
|
end
|
||||||
|
|
||||||
function handle:delete()
|
function handle:delete()
|
||||||
world():delete(self.entity)
|
self.world:delete(self.entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
function handle:id(): entity
|
function handle:id(): entity
|
||||||
|
|
11
lib/ref.luau
11
lib/ref.luau
|
@ -1,9 +1,10 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
local handle = require("./handle")
|
local handle = require("./handle")
|
||||||
|
local jecs = require("./jecs")
|
||||||
local WORLD = require("./world").get
|
local WORLD = require("./world").get
|
||||||
|
|
||||||
local refs = {}
|
local refs: { [jecs.World]: { [any]: jecs.Entity } } = {}
|
||||||
|
|
||||||
--- Gets an entity the given key references to.
|
--- Gets an entity the given key references to.
|
||||||
--- If the key is nil, an entirely new entity is created and returned.
|
--- If the key is nil, an entirely new entity is created and returned.
|
||||||
|
@ -16,10 +17,14 @@ local function ref(key: any): handle.handle
|
||||||
return handle(world:entity())
|
return handle(world:entity())
|
||||||
end
|
end
|
||||||
|
|
||||||
local entity = refs[key]
|
if not refs[world] then
|
||||||
|
refs[world] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity = refs[world][key]
|
||||||
if not entity then
|
if not entity then
|
||||||
entity = world:entity()
|
entity = world:entity()
|
||||||
refs[key] = entity
|
refs[world][key] = entity
|
||||||
end
|
end
|
||||||
|
|
||||||
return handle(entity)
|
return handle(entity)
|
||||||
|
|
|
@ -4,15 +4,26 @@ local jecs = require("./jecs")
|
||||||
|
|
||||||
local WORLD: jecs.World
|
local WORLD: jecs.World
|
||||||
|
|
||||||
|
local listeners: { (jecs.World) -> () } = {}
|
||||||
|
|
||||||
local function get(): jecs.World
|
local function get(): jecs.World
|
||||||
return WORLD
|
return WORLD
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set(world: jecs.World)
|
local function set(world: jecs.World)
|
||||||
WORLD = world
|
WORLD = world
|
||||||
|
|
||||||
|
for _, fn in listeners do
|
||||||
|
fn(world)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_set(fn: (jecs.World) -> ())
|
||||||
|
table.insert(listeners, fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get = get,
|
get = get,
|
||||||
set = set,
|
set = set,
|
||||||
|
on_set = on_set,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue