mirror of
https://github.com/Ukendio/jecs.git
synced 2025-09-23 08:39:16 +00:00
Compare commits
No commits in common. "main" and "v0.9.0" have entirely different histories.
7 changed files with 174 additions and 517 deletions
145
addons/ob.luau
145
addons/ob.luau
|
@ -12,10 +12,6 @@ export type Iter<T...> = (Observer<T...>) -> () -> (jecs.Entity, T...)
|
||||||
|
|
||||||
export type Observer<T...> = {
|
export type Observer<T...> = {
|
||||||
disconnect: (Observer<T...>) -> (),
|
disconnect: (Observer<T...>) -> (),
|
||||||
}
|
|
||||||
|
|
||||||
export type Monitor<T...> = {
|
|
||||||
disconnect: (Observer<T...>) -> (),
|
|
||||||
added: ((jecs.Entity) -> ()) -> (),
|
added: ((jecs.Entity) -> ()) -> (),
|
||||||
removed: ((jecs.Entity) -> ()) -> ()
|
removed: ((jecs.Entity) -> ()) -> ()
|
||||||
}
|
}
|
||||||
|
@ -26,12 +22,11 @@ local function observers_new<T...>(
|
||||||
): Observer<T...>
|
): Observer<T...>
|
||||||
query:cached()
|
query:cached()
|
||||||
|
|
||||||
|
|
||||||
local world = (query :: Query<T...> & { world: World }).world
|
local world = (query :: Query<T...> & { world: World }).world
|
||||||
callback = callback
|
callback = callback
|
||||||
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
local terms = query.filter_with :: { jecs.Id }
|
local terms = query.ids
|
||||||
local first = terms[1]
|
local first = terms[1]
|
||||||
|
|
||||||
local observers_on_create = world.observable[jecs.ArchetypeCreate][first]
|
local observers_on_create = world.observable[jecs.ArchetypeCreate][first]
|
||||||
|
@ -47,10 +42,6 @@ local function observers_new<T...>(
|
||||||
|
|
||||||
local entity_index = world.entity_index :: any
|
local entity_index = world.entity_index :: any
|
||||||
|
|
||||||
for _, archetype in query:archetypes() do
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function emplaced<T, a>(
|
local function emplaced<T, a>(
|
||||||
entity: jecs.Entity<T>,
|
entity: jecs.Entity<T>,
|
||||||
id: jecs.Id<a>,
|
id: jecs.Id<a>,
|
||||||
|
@ -81,38 +72,19 @@ local function observers_new<T...>(
|
||||||
if without then
|
if without then
|
||||||
for _, term in without do
|
for _, term in without do
|
||||||
if jecs.IS_PAIR(term) then
|
if jecs.IS_PAIR(term) then
|
||||||
local rel = jecs.ECS_PAIR_FIRST(term)
|
term = jecs.ECS_PAIR_FIRST(term)
|
||||||
local tgt = jecs.ECS_PAIR_SECOND(term)
|
|
||||||
local wc = tgt == jecs.w
|
|
||||||
local onremoved = world:removed(rel, function(entity, id)
|
|
||||||
if not wc and id ~= term then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
table.insert(cleanup, onremoved)
|
|
||||||
else
|
|
||||||
local onremoved = world:removed(term, function(entity, id)
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
table.insert(cleanup, onremoved)
|
|
||||||
end
|
end
|
||||||
|
local onremoved = world:removed(term, function(entity, id)
|
||||||
|
local r = jecs.record(world, entity)
|
||||||
|
local archetype = r.archetype
|
||||||
|
if archetype then
|
||||||
|
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||||
|
if archetypes[dst.id] then
|
||||||
|
callback(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
table.insert(cleanup, onremoved)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -141,7 +113,7 @@ local function observers_new<T...>(
|
||||||
return (observer :: any) :: Observer<T...>
|
return (observer :: any) :: Observer<T...>
|
||||||
end
|
end
|
||||||
|
|
||||||
local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
local function monitors_new<T...>(query: Query<T...>): Observer<T...>
|
||||||
query:cached()
|
query:cached()
|
||||||
|
|
||||||
local world = (query :: Query<T...> & { world: World }).world
|
local world = (query :: Query<T...> & { world: World }).world
|
||||||
|
@ -160,10 +132,6 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
observer_on_delete.callback = function(archetype)
|
observer_on_delete.callback = function(archetype)
|
||||||
archetypes[archetype.id] = nil
|
archetypes[archetype.id] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, archetype in query:archetypes() do
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
local entity_index = world.entity_index :: any
|
local entity_index = world.entity_index :: any
|
||||||
|
|
||||||
local callback_added: ((jecs.Entity) -> ())?
|
local callback_added: ((jecs.Entity) -> ())?
|
||||||
|
@ -215,73 +183,24 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
if without then
|
if without then
|
||||||
for _, term in without do
|
for _, term in without do
|
||||||
if jecs.IS_PAIR(term) then
|
if jecs.IS_PAIR(term) then
|
||||||
local rel = jecs.ECS_PAIR_FIRST(term)
|
term = jecs.ECS_PAIR_FIRST(term)
|
||||||
local tgt = jecs.ECS_PAIR_SECOND(term)
|
|
||||||
local wc = tgt == jecs.w
|
|
||||||
local onadded = world:added(rel, function(entity, id)
|
|
||||||
if callback_removed == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not wc and id ~= term then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback_removed(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
local onremoved = world:removed(rel, function(entity, id)
|
|
||||||
if callback_added == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not wc and id ~= term then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback_added(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
table.insert(cleanup, onadded)
|
|
||||||
table.insert(cleanup, onremoved)
|
|
||||||
else
|
|
||||||
local onadded = world:added(term, function(entity, id)
|
|
||||||
if callback_removed == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback_removed(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
local onremoved = world:removed(term, function(entity, id)
|
|
||||||
if callback_added == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local r = jecs.record(world, entity)
|
|
||||||
local archetype = r.archetype
|
|
||||||
if archetype then
|
|
||||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
|
||||||
if archetypes[dst.id] then
|
|
||||||
callback_added(entity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
table.insert(cleanup, onadded)
|
|
||||||
table.insert(cleanup, onremoved)
|
|
||||||
end
|
end
|
||||||
|
local onadded = world:added(term, removed)
|
||||||
|
local onremoved = world:removed(term, function(entity, id)
|
||||||
|
if callback_added == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local r = jecs.record(world, entity)
|
||||||
|
local archetype = r.archetype
|
||||||
|
if archetype then
|
||||||
|
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||||
|
if archetypes[dst.id] then
|
||||||
|
callback_added(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
table.insert(cleanup, onadded)
|
||||||
|
table.insert(cleanup, onremoved)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -317,7 +236,7 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
removed = monitor_removed
|
removed = monitor_removed
|
||||||
}
|
}
|
||||||
|
|
||||||
return (observer :: any) :: Monitor<T...>
|
return (observer :: any) :: Observer<T...>
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
-- Using world:target is the recommended way to grab the target in a wildcard
|
-- Using world:target is the recommended way to grab the target in a wildcard
|
||||||
-- query. However the random access can add up in very hot paths. Accessing its
|
-- query. However the random access can add up in very hot paths. Accessing its
|
||||||
-- column can drastically improve performance, especially when there are
|
-- column can drastically improve performance, especially when there are
|
||||||
-- multiple adjacent pairs.
|
-- multiple adjacent pairs.
|
||||||
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
|
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
|
|
||||||
local Likes = world:entity()
|
local Likes = world:entity()
|
||||||
local function name(e, name: string): string
|
local function name(e, name: string): string
|
||||||
if name then
|
if name then
|
||||||
world:set(e, jecs.Name, name)
|
world:set(e, jecs.Name, name)
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
return assert(world:get(e, jecs.Name))
|
return assert(world:get(e, jecs.Name))
|
||||||
end
|
end
|
||||||
|
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
name(e1, "e1")
|
name(e1, "e1")
|
||||||
local e2 = world:entity()
|
local e2 = world:entity()
|
||||||
name(e2, "e2")
|
name(e2, "e2")
|
||||||
local e3 = world:entity()
|
local e3 = world:entity()
|
||||||
name(e3, "e3")
|
name(e3, "e3")
|
||||||
|
|
||||||
world:add(e1, pair(Likes, e2))
|
world:add(e1, pair(Likes, e2))
|
||||||
world:add(e1, pair(Likes, e3))
|
world:add(e1, pair(Likes, e3))
|
||||||
|
|
||||||
local likes = jecs.component_record(world, pair(Likes, __))
|
local likes = jecs.component_record(world, pair(Likes, __))
|
||||||
assert(likes)
|
assert(likes)
|
||||||
|
|
||||||
local likes_cr = likes.records
|
local likes_cr = likes.records
|
||||||
local likes_counts = likes.counts
|
local likes_counts = likes.counts
|
||||||
|
|
||||||
local archetypes = world:query(pair(Likes, __)):archetypes()
|
local archetypes = world:query(pair(Likes, __)):archetypes()
|
||||||
|
|
||||||
for _, archetype in archetypes do
|
for _, archetype in archetypes do
|
||||||
local types = archetype.types
|
local types = archetype.types
|
||||||
|
|
||||||
-- Get the starting index which is what the (R, *) alias is at
|
-- Get the starting index which is what the (R, *) alias is at
|
||||||
local wc = likes_cr[archetype.id]
|
local wc = likes_cr[archetype.id]
|
||||||
local count = likes_counts[archetype.id]
|
local count = likes_counts[archetype.id]
|
||||||
|
|
||||||
local entities = archetype.entities
|
local entities = archetype.entities
|
||||||
for i = #entities, 1, -1 do
|
for i = #entities, 1, -1 do
|
||||||
-- It is generally a good idea to iterate backwards on arrays if you
|
-- It is generally a good idea to iterate backwards on arrays if you
|
||||||
-- need to delete the iterated entity to prevent iterator invalidation
|
-- need to delete the iterated entity to prevent iterator invalidation
|
||||||
local entity = entities[i]
|
local entity = entities[i]
|
||||||
for cr = wc, wc + count - 1 do
|
for cr = wc, wc + count - 1 do
|
||||||
local person = jecs.pair_second(world, types[cr])
|
local person = jecs.pair_second(world, types[cr])
|
||||||
print(`entity ${entity} likes ${person}`)
|
print(`entity ${entity} likes ${person}`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Output:
|
-- Output:
|
||||||
-- entity $273 likes $275
|
-- entity $273 likes $275
|
||||||
-- entity $273 likes $274
|
-- entity $273 likes $274
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
-- To get the most out of performance, you can lift the inner loop of queries to
|
-- To get the most out of performance, you can lift the inner loop of queries to
|
||||||
-- the system in which you can do archetype-specific optimizations like finding
|
-- the system in which you can do archetype-specific optimizations like finding
|
||||||
-- the parent once per archetype rather than per entity.
|
-- the parent once per archetype rather than per entity.
|
||||||
|
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
local ChildOf = jecs.ChildOf
|
local ChildOf = jecs.ChildOf
|
||||||
local __ = jecs.Wildcard
|
local __ = jecs.Wildcard
|
||||||
|
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
|
|
||||||
local Position = world:component() :: jecs.Id<vector>
|
local Position = world:component() :: jecs.Id<vector>
|
||||||
local Visible = world:entity()
|
local Visible = world:entity()
|
||||||
|
|
||||||
local parent = world:entity()
|
local parent = world:entity()
|
||||||
world:set(parent, Position, vector.zero)
|
world:set(parent, Position, vector.zero)
|
||||||
world:add(parent, Visible)
|
world:add(parent, Visible)
|
||||||
|
|
||||||
local child = world:entity()
|
local child = world:entity()
|
||||||
world:set(child, Position, vector.one)
|
world:set(child, Position, vector.one)
|
||||||
world:add(child, pair(ChildOf, parent))
|
world:add(child, pair(ChildOf, parent))
|
||||||
|
|
||||||
local parents = jecs.component_record(world, pair(ChildOf, __))
|
local parents = jecs.component_record(world, pair(ChildOf, __))
|
||||||
assert(parents)
|
assert(parents)
|
||||||
|
|
||||||
local parent_cr = parents.records
|
local parent_cr = parents.records
|
||||||
|
|
||||||
local archetypes = world:query(Position, pair(ChildOf, __)):archetypes()
|
local archetypes = world:query(Position, pair(ChildOf, __)):archetypes()
|
||||||
|
|
||||||
for _, archetype in archetypes do
|
for _, archetype in archetypes do
|
||||||
local types = archetype.types
|
local types = archetype.types
|
||||||
local p = jecs.pair_second(world, types[parent_cr[archetype.id]])
|
local p = jecs.pair_second(world, types[parent_cr[archetype.id]])
|
||||||
if world:has(p, Visible) then
|
if world:has(p, Visible) then
|
||||||
local columns = archetype.columns_map
|
local columns = archetype.columns_map
|
||||||
local positions = columns[Position]
|
local positions = columns[Position]
|
||||||
for row, entity in archetype.entities do
|
for row, entity in archetype.entities do
|
||||||
local pos = positions[row]
|
local pos = positions[row]
|
||||||
print(`Child ${entity} of ${p} is visible at {pos}`)
|
print(`Child ${entity} of ${p} is visible at {pos}`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Output:
|
-- Output:
|
||||||
-- Child $274 of $273 is visibile at 1,1,1
|
-- Child $274 of $273 is visibile at 1,1,1
|
||||||
|
|
15
jecs.d.ts
vendored
15
jecs.d.ts
vendored
|
@ -236,12 +236,6 @@ export class World {
|
||||||
*/
|
*/
|
||||||
contains(entity: Entity): boolean;
|
contains(entity: Entity): boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an entity exists in the world.
|
|
||||||
* @param entity The entity to verify.
|
|
||||||
*/
|
|
||||||
contains(entity: number): entity is Entity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an entity with the given ID is currently alive, ignoring its generation.
|
* Checks if an entity with the given ID is currently alive, ignoring its generation.
|
||||||
* @param entity The entity to verify.
|
* @param entity The entity to verify.
|
||||||
|
@ -350,19 +344,12 @@ export type ComponentRecord = {
|
||||||
export function component_record(world: World, id: Id): ComponentRecord;
|
export function component_record(world: World, id: Id): ComponentRecord;
|
||||||
|
|
||||||
type TagToUndefined<T> = T extends TagDiscriminator ? undefined : T
|
type TagToUndefined<T> = T extends TagDiscriminator ? undefined : T
|
||||||
type TrimOptional<T extends unknown[]> = T extends [...infer L, infer R]
|
|
||||||
? unknown extends R
|
|
||||||
? L | T | TrimOptional<L>
|
|
||||||
: R extends undefined
|
|
||||||
? L | T | TrimOptional<L>
|
|
||||||
: T
|
|
||||||
: T
|
|
||||||
|
|
||||||
export function bulk_insert<const C extends Id[]>(
|
export function bulk_insert<const C extends Id[]>(
|
||||||
world: World,
|
world: World,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
ids: C,
|
ids: C,
|
||||||
values: TrimOptional<{ [K in keyof C]: TagToUndefined<InferComponent<C[K]>> }>,
|
values: { [K in keyof C]: TagToUndefined<InferComponent<C[K]>> },
|
||||||
): void;
|
): void;
|
||||||
export function bulk_remove(world: World, entity: Entity, ids: Id[]): void;
|
export function bulk_remove(world: World, entity: Entity, ids: Id[]): void;
|
||||||
|
|
||||||
|
|
|
@ -1607,9 +1607,6 @@ local function query_cached(query: QueryInner)
|
||||||
local lastArchetype = 1
|
local lastArchetype = 1
|
||||||
|
|
||||||
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id })
|
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id })
|
||||||
if not A then
|
|
||||||
A = query.filter_with[1]
|
|
||||||
end
|
|
||||||
local a: Column, b: Column, c: Column, d: Column
|
local a: Column, b: Column, c: Column, d: Column
|
||||||
local e: Column, f: Column, g: Column, h: Column
|
local e: Column, f: Column, g: Column, h: Column
|
||||||
|
|
||||||
|
@ -3247,7 +3244,8 @@ local function world_new()
|
||||||
return world
|
return world
|
||||||
end
|
end
|
||||||
|
|
||||||
-- type function ecs_id_t(ty)
|
-- type function ecs_id_t(entity)
|
||||||
|
-- local ty = entity:components()[2]
|
||||||
-- local __T = ty:readproperty(types.singleton("__T"))
|
-- local __T = ty:readproperty(types.singleton("__T"))
|
||||||
-- if not __T then
|
-- if not __T then
|
||||||
-- return ty:readproperty(types.singleton("__jecs_pair_value"))
|
-- return ty:readproperty(types.singleton("__jecs_pair_value"))
|
||||||
|
|
|
@ -5,109 +5,21 @@ local CASE, TEST, FINISH, CHECK = test.CASE, test.TEST, test.FINISH, test.CHECK
|
||||||
local FOCUS = test.FOCUS
|
local FOCUS = test.FOCUS
|
||||||
local ob = require("@addons/ob")
|
local ob = require("@addons/ob")
|
||||||
|
|
||||||
TEST("addons/ob::observer", function()
|
TEST("addons/ob", function()
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
do CASE "should match against archetypes that were already created"
|
do CASE "Should support query:without()"
|
||||||
local A = world:component()
|
|
||||||
|
|
||||||
local e1 = world:entity()
|
|
||||||
world:add(e1, A)
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
ob.observer(world:query(A), function()
|
|
||||||
c+=1
|
|
||||||
end)
|
|
||||||
|
|
||||||
world:remove(e1, A)
|
|
||||||
world:add(e1, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter observer at query:without(pair(R, t1)) when adding pair(R, t2)"
|
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
world:add(e, A)
|
|
||||||
world:add(e, jecs.pair(B, D))
|
|
||||||
|
|
||||||
local c = 1
|
local c = 1
|
||||||
|
local monitor = ob.monitor(world:query(A):without(B))
|
||||||
ob.observer(world:query(A):without(jecs.pair(B, C)), function()
|
monitor.added(function()
|
||||||
c += 1
|
c += 1
|
||||||
end)
|
end)
|
||||||
|
monitor.removed(function()
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter observer at query:without(pair(R, t1)) when removing pair(R, t1)"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
world:add(e, A)
|
|
||||||
world:add(e, jecs.pair(B, D))
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
ob.observer(world:query(A):without(jecs.pair(B, D)), function()
|
|
||||||
c += 1
|
c += 1
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==1)
|
|
||||||
world:remove(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:remove(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:remove(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:remove(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==3)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter observer at query:without(pair(R, *)) when adding pair(R, t1)"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
ob.observer(world:query(A):without(jecs.pair(B, jecs.w)), function() c+= 1 end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter observer at query:without(t1) when removing t1"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
ob.observer(world:query(A):without(B), function() c+= 1 end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
local child = world:entity()
|
||||||
world:add(child, B)
|
world:add(child, B)
|
||||||
CHECK(c==1)
|
CHECK(c==1)
|
||||||
|
@ -116,22 +28,28 @@ TEST("addons/ob::observer", function()
|
||||||
world:remove(child, B)
|
world:remove(child, B)
|
||||||
CHECK(c==2)
|
CHECK(c==2)
|
||||||
world:remove(child, A)
|
world:remove(child, A)
|
||||||
CHECK(c==2)
|
CHECK(c==3)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "observers should accept pairs"
|
do CASE "monitors should accept pairs"
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
|
|
||||||
local c = 1
|
local c = 1
|
||||||
ob.observer(world:query(jecs.pair(A, B)), function() c+= 1 end)
|
local monitor = ob.monitor(world:query(jecs.pair(A, B)))
|
||||||
|
monitor.added(function()
|
||||||
|
c += 1
|
||||||
|
end)
|
||||||
|
monitor.removed(function()
|
||||||
|
c += 1
|
||||||
|
end)
|
||||||
|
|
||||||
local child = world:entity()
|
local child = world:entity()
|
||||||
world:add(child, jecs.pair(A, B))
|
world:add(child, jecs.pair(A, B))
|
||||||
CHECK(c == 2)
|
CHECK(c == 2)
|
||||||
|
|
||||||
world:remove(child, jecs.pair(A, B))
|
world:remove(child, jecs.pair(A, B))
|
||||||
CHECK(c == 2)
|
CHECK(c == 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Ensure ordering between signals and observers"
|
do CASE "Ensure ordering between signals and observers"
|
||||||
|
@ -181,191 +99,6 @@ TEST("addons/ob::observer", function()
|
||||||
CHECK(count == 4)
|
CHECK(count == 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Call off pairs"
|
|
||||||
local A = world:component()
|
|
||||||
|
|
||||||
local callcount = 1
|
|
||||||
world:added(A, function(entity)
|
|
||||||
callcount += 1
|
|
||||||
end)
|
|
||||||
world:added(A, function(entity)
|
|
||||||
callcount += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
local e2 = world:entity()
|
|
||||||
|
|
||||||
world:add(e2, jecs.pair(A, e))
|
|
||||||
world:add(e, jecs.pair(A, e2))
|
|
||||||
CHECK(callcount == 1 + 2 * 2)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
TEST("addons/ob::monitor", function()
|
|
||||||
local world = jecs.world()
|
|
||||||
do CASE "should match against archetypes that were already created"
|
|
||||||
local A = world:component()
|
|
||||||
|
|
||||||
local e1 = world:entity()
|
|
||||||
world:add(e1, A)
|
|
||||||
|
|
||||||
local monitor = ob.monitor(world:query(A))
|
|
||||||
local c = 1
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
world:remove(e1, A)
|
|
||||||
world:add(e1, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter monitor at query:without(pair(R, t1)) when adding pair(R, t2)"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
world:add(e, A)
|
|
||||||
world:add(e, jecs.pair(B, D))
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
local r = 1
|
|
||||||
local monitor = ob.monitor(world:query(A):without(jecs.pair(B, C)))
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
monitor.removed(function()
|
|
||||||
r += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==1)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter monitor at query:without(pair(R, t1)) when removing pair(R, t1)"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
world:add(e, A)
|
|
||||||
world:add(e, jecs.pair(B, D))
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
local r = 1
|
|
||||||
local monitor = ob.monitor(world:query(A):without(jecs.pair(B, D)))
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
monitor.removed(function()
|
|
||||||
r += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==1)
|
|
||||||
world:remove(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==1)
|
|
||||||
world:remove(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==2)
|
|
||||||
world:remove(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==2)
|
|
||||||
world:remove(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==3)
|
|
||||||
CHECK(r==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter monitor at query:without(pair(R, *)) when adding pair(R, t1)"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
local r = 1
|
|
||||||
local monitor = ob.monitor(world:query(A):without(jecs.pair(B, jecs.w)))
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
monitor.removed(function()
|
|
||||||
r += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==2)
|
|
||||||
world:add(child, jecs.pair(B, D))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==2)
|
|
||||||
world:add(child, jecs.pair(B, C))
|
|
||||||
CHECK(c==2)
|
|
||||||
CHECK(r==2)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should enter monitor at query:without(t1) when removing t1"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
local monitor = ob.monitor(world:query(A):without(B))
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
monitor.removed(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, B)
|
|
||||||
CHECK(c==1)
|
|
||||||
world:add(child, A)
|
|
||||||
CHECK(c==1)
|
|
||||||
world:remove(child, B)
|
|
||||||
CHECK(c==2)
|
|
||||||
world:remove(child, A)
|
|
||||||
CHECK(c==3)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "monitors should accept pairs"
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
|
|
||||||
local c = 1
|
|
||||||
local monitor = ob.monitor(world:query(jecs.pair(A, B)))
|
|
||||||
monitor.added(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
monitor.removed(function()
|
|
||||||
c += 1
|
|
||||||
end)
|
|
||||||
|
|
||||||
local child = world:entity()
|
|
||||||
world:add(child, jecs.pair(A, B))
|
|
||||||
CHECK(c == 2)
|
|
||||||
|
|
||||||
world:remove(child, jecs.pair(A, B))
|
|
||||||
CHECK(c == 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Don't report changed components in monitor"
|
do CASE "Don't report changed components in monitor"
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
local count = 1
|
local count = 1
|
||||||
|
@ -387,6 +120,25 @@ TEST("addons/ob::monitor", function()
|
||||||
world:set(e, A, false)
|
world:set(e, A, false)
|
||||||
CHECK(count == 4)
|
CHECK(count == 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do CASE "Call off pairs"
|
||||||
|
local A = world:component()
|
||||||
|
|
||||||
|
local callcount = 1
|
||||||
|
world:added(A, function(entity)
|
||||||
|
callcount += 1
|
||||||
|
end)
|
||||||
|
world:added(A, function(entity)
|
||||||
|
callcount += 1
|
||||||
|
end)
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
|
||||||
|
world:add(e2, jecs.pair(A, e))
|
||||||
|
world:add(e, jecs.pair(A, e2))
|
||||||
|
CHECK(callcount == 1 + 2 * 2)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return FINISH()
|
return FINISH()
|
||||||
|
|
|
@ -1365,6 +1365,7 @@ TEST("world:added", function()
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE ""
|
do CASE ""
|
||||||
|
local world = jecs.world()
|
||||||
local IsNearby = world:component()
|
local IsNearby = world:component()
|
||||||
world:set(IsNearby, jecs.Name, "IsNearby")
|
world:set(IsNearby, jecs.Name, "IsNearby")
|
||||||
local person1, person2 = world:entity(), world:entity()
|
local person1, person2 = world:entity(), world:entity()
|
||||||
|
|
Loading…
Reference in a new issue