Fix world_has_any checking for inverse statement

This commit is contained in:
Ukendio 2024-08-17 00:53:27 +02:00
parent 6d45af93f0
commit 4092a6c855
2 changed files with 125 additions and 31 deletions

View file

@ -365,7 +365,7 @@ local function world_has_any(world: World, entity: number, ...: i53): boolean
local records = archetype.records local records = archetype.records
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
if not records[select(i, ...)] then if records[select(i, ...)] then
return true return true
end end
end end
@ -1407,7 +1407,7 @@ World.target = world_target
World.parent = world_parent World.parent = world_parent
World.contains = world_contains World.contains = world_contains
function World.new(): t_world function World.new(): self: World
local self = setmetatable({ local self = setmetatable({
archetypeIndex = {} :: { [string]: Archetype }, archetypeIndex = {} :: { [string]: Archetype },
archetypes = {} :: Archetypes, archetypes = {} :: Archetypes,
@ -1464,44 +1464,40 @@ type World = {
nextComponentId: number, nextComponentId: number,
nextEntityId: number, nextEntityId: number,
nextArchetypeId: number, nextArchetypeId: number,
} } & {
export type t_world = typeof(setmetatable(
{} :: {
--- Creates a new entity --- Creates a new entity
entity: (t_world) -> Entity, entity: (self: World) -> Entity,
--- Creates a new entity located in the first 256 ids. --- Creates a new entity located in the first 256 ids.
--- These should be used for static components for fast access. --- These should be used for static components for fast access.
component: <T>(t_world) -> Entity<T>, component: <T>(self: World) -> Entity<T>,
--- Gets the target of an relationship. For example, when a user calls --- Gets the target of an relationship. For example, when a user calls
--- `world:target(id, ChildOf(parent))`, you will obtain the parent entity. --- `world:target(id, ChildOf(parent))`, you will obtain the parent entity.
target: (t_world, id: Entity, relation: Entity) -> Entity?, target: (self: World, id: Entity, relation: Entity) -> Entity?,
--- Deletes an entity and all it's related components and relationships. --- Deletes an entity and all it's related components and relationships.
delete: (t_world, id: Entity) -> (), delete: (self: World, id: Entity) -> (),
--- Adds a component to the entity with no value --- Adds a component to the entity with no value
add: <T>(world: t_world, id: Entity, component: Entity<T>) -> (), add: <T>(self: World, id: Entity, component: Entity<T>) -> (),
--- Assigns a value to a component on the given entity --- Assigns a value to a component on the given entity
set: <T>(world: World, id: Entity, component: Entity<T>, data: T) -> (), set: <T>(self: World, id: Entity, component: Entity<T>, data: T) -> (),
-- Clears an entity from the world -- Clears an entity from the world
clear: (t_world, id: Entity) -> (), clear: (self: World, id: Entity) -> (),
--- Removes a component from the given entity --- Removes a component from the given entity
remove: (t_world, id: Entity, component: Entity) -> (), remove: (self: World, id: Entity, component: Entity) -> (),
--- Retrieves the value of up to 4 components. These values may be nil. --- Retrieves the value of up to 4 components. These values may be nil.
get: (<A>(t_world, id: any, Entity<A>) -> A) get: (<A>(self: World, id: any, Entity<A>) -> A)
& (<A, B>(t_world, id: Entity, Entity<A>, Entity<B>) -> (A, B)) & (<A, B>(self: World, id: Entity, Entity<A>, Entity<B>) -> (A, B))
& (<A, B, C>(t_world, id: Entity, Entity<A>, Entity<B>, Entity<C>) -> (A, B, C)) & (<A, B, C>(self: World, id: Entity, Entity<A>, Entity<B>, Entity<C>) -> (A, B, C))
& <A, B, C, D>(t_world, id: Entity, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> (A, B, C, D), & <A, B, C, D>(self: World, id: Entity, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> (A, B, C, D),
--- Searches the world for entities that match a given query --- Searches the world for entities that match a given query
query: (<A>(t_world, Entity<A>) -> Query<A>) query: (<A>(self: World, Entity<A>) -> Query<A>)
& (<A, B>(t_world, Entity<A>, Entity<B>) -> Query<A, B>) & (<A, B>(self: World, Entity<A>, Entity<B>) -> Query<A, B>)
& (<A, B, C>(t_world, Entity<A>, Entity<B>, Entity<C>) -> Query<A, B, C>) & (<A, B, C>(self: World, Entity<A>, Entity<B>, Entity<C>) -> Query<A, B, C>)
& (<A, B, C, D>(t_world, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> Query<A, B, C, D>) & (<A, B, C, D>(self: World, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> Query<A, B, C, D>)
& (<A, B, C, D, E>( & (<A, B, C, D, E>(
t_world, self: World,
Entity<A>, Entity<A>,
Entity<B>, Entity<B>,
Entity<C>, Entity<C>,
@ -1509,7 +1505,7 @@ export type t_world = typeof(setmetatable(
Entity<E> Entity<E>
) -> Query<A, B, C, D, E>) ) -> Query<A, B, C, D, E>)
& (<A, B, C, D, E, F>( & (<A, B, C, D, E, F>(
t_world, self: World,
Entity<A>, Entity<A>,
Entity<B>, Entity<B>,
Entity<C>, Entity<C>,
@ -1518,7 +1514,7 @@ export type t_world = typeof(setmetatable(
Entity<F> Entity<F>
) -> Query<A, B, C, D, E, F>) ) -> Query<A, B, C, D, E, F>)
& (<A, B, C, D, E, F, G>( & (<A, B, C, D, E, F, G>(
t_world, self: World,
Entity<A>, Entity<A>,
Entity<B>, Entity<B>,
Entity<C>, Entity<C>,
@ -1528,7 +1524,7 @@ export type t_world = typeof(setmetatable(
Entity<G> Entity<G>
) -> Query<A, B, C, D, E, F, G>) ) -> Query<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>( & (<A, B, C, D, E, F, G, H>(
t_world, self: World,
Entity<A>, Entity<A>,
Entity<B>, Entity<B>,
Entity<C>, Entity<C>,
@ -1539,12 +1535,10 @@ export type t_world = typeof(setmetatable(
Entity<H>, Entity<H>,
...Entity<any> ...Entity<any>
) -> Query<A, B, C, D, E, F, G, H>), ) -> Query<A, B, C, D, E, F, G, H>),
}, {} }
))
return { return {
World = World :: { new: () -> t_world }, World = World :: { new: () -> World },
OnAdd = EcsOnAdd :: Entity, OnAdd = EcsOnAdd :: Entity,
OnRemove = EcsOnRemove :: Entity, OnRemove = EcsOnRemove :: Entity,

View file

@ -1142,5 +1142,105 @@ TEST("Hooks", function()
CHECK(not world:has(e1, A)) CHECK(not world:has(e1, A))
end end
do CASE "the filip incident"
local world = jecs.World.new()
export type Iterator<T> = () -> (Entity, T?, T?)
export type Destructor = () -> ()
-- Helpers
type ValuesMap<T> = { [Entity]: T? }
type ChangeSet = { [Entity]: true? }
type ChangeSets = { [ChangeSet]: true? }
type ChangeSetsCache = {
Added: ChangeSets,
Changed: ChangeSets,
Removed: ChangeSets,
}
local cachedChangeSets = {}
local function getChangeSets(component): ChangeSetsCache
if cachedChangeSets[component] == nil then
local changeSetsAdded: ChangeSets = {}
local changeSetsChanged: ChangeSets = {}
local changeSetsRemoved: ChangeSets = {}
world:set(component, jecs.OnAdd, function(id)
for set in changeSetsAdded do
set[id] = true
end
end)
world:set(component, jecs.OnSet, function(id)
for set in changeSetsChanged do
set[id] = true
end
end)
world:set(component, jecs.OnRemove, function(id)
for set in changeSetsRemoved do
set[id] = true
end
end)
cachedChangeSets[component] = {
Added = changeSetsAdded,
Changed = changeSetsChanged,
Removed = changeSetsRemoved,
}
end
return cachedChangeSets[component]
end
local function ChangeTracker<T>(component): (Iterator<T>, Destructor)
local values: ValuesMap<T> = {}
local changeSet: ChangeSet = {}
for id in world:query(component) do
changeSet[id] = true
end
local changeSets = getChangeSets(component)
changeSets.Added[changeSet] = true
changeSets.Changed[changeSet] = true
changeSets.Removed[changeSet] = true
local id: Entity? = nil
local iter: Iterator<T> = function()
id = next(changeSet)
if id then
changeSet[id] = nil
local old: T? = values[id]
local new: T? = world:get(id, component)
if old ~= nil and new == nil then
-- Old value but no new value = removed
values[id] = nil
else
-- Old+new value or just new value = new becomes old
values[id] = new
end
return id, old, new
end
return nil :: any, nil, nil
end
local destroy: Destructor = function()
changeSets.Added[changeSet] = nil
changeSets.Changed[changeSet] = nil
changeSets.Removed[changeSet] = nil
end
return iter, destroy
end
local Transform = world:component()
local iter, destroy = ChangeTracker(Transform)
local e1 = world:entity()
world:set(e1, Transform, {1,1})
local counter = 0
for _ in iter do
counter += 1
end
CHECK(counter == 1)
end
end) end)
FINISH() FINISH()