Remove multret (#99)

* Initial commit

* Move functions for get and has

* Update docs
This commit is contained in:
Marcus 2024-08-11 03:27:29 +02:00 committed by GitHub
parent a2dedf128a
commit e6983a3356
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 109 additions and 260 deletions

View file

@ -70,12 +70,21 @@ For example, a Health type should be created using this.
### get() ### get()
```luau ```luau
function World:get( function World:get<T>(
entity: Entity, -- The entity entity: Entity, -- The entity
...: Entity<T> -- The types to fetch id: Entity<T> -- The component ID to fetch
): ... -- Returns the component data in the same order they were passed in ): T
``` ```
Returns the data for each provided type for the corresponding entity. Returns the data for the component data the corresponding entity, nil if entity does not have the ID or was a tag.
### has()
```luau
function World:has(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to check
): boolean
```
Returns whether the entity has the ID.
::: :::
@ -109,7 +118,7 @@ Adds or changes the entity's component.
```luau ```luau
function World:query( function World:query(
...: Entity -- The IDs to query with ...: Entity -- The IDs to query with
): Query -- Returns the Query ): Query
``` ```
Creates a [`query`](query) with the given IDs. Entities that satisfies the conditions of the query will be returned and their corresponding data. Creates a [`query`](query) with the given IDs. Entities that satisfies the conditions of the query will be returned and their corresponding data.

View file

@ -56,17 +56,6 @@ type ArchetypeDiff = {
removed: Ty, removed: Ty,
} }
export type World = {
archetypeIndex: { [string]: Archetype },
archetypes: Archetypes,
componentIndex: ComponentIndex,
entityIndex: EntityIndex,
nextArchetypeId: number,
nextComponentId: number,
nextEntityId: number,
ROOT_ARCHETYPE: Archetype
}
local HI_COMPONENT_ID = 256 local HI_COMPONENT_ID = 256
local EcsOnAdd = HI_COMPONENT_ID + 1 local EcsOnAdd = HI_COMPONENT_ID + 1
@ -375,6 +364,38 @@ local function world_parent(world: World, entity: i53)
return world_target(world, entity, EcsChildOf) return world_target(world, entity, EcsChildOf)
end end
local function world_get(world: World, entity: i53, id: i53)
local record = world.entityIndex.sparse[entity]
if not record then
return nil
end
local archetype = record.archetype
if not archetype then
return nil
end
local tr = archetype.records[id]
if not tr then
return nil
end
return archetype.columns[tr.column][record.row]
end
local function world_has(world: World, entity: i53, id: i53): boolean
local record = world.entityIndex.sparse[entity]
if not record then
return false
end
local archetype = record.archetype
if not archetype then
return false
end
return archetype.records[id] ~= nil
end
local function archetype_ensure(world: World, types, prev): Archetype local function archetype_ensure(world: World, types, prev): Archetype
if #types < 1 then if #types < 1 then
return world.ROOT_ARCHETYPE return world.ROOT_ARCHETYPE
@ -620,88 +641,6 @@ local function world_clear(world: World, entity: i53)
entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE) entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE)
end end
local world_get: (world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?) -> (...any)
do
-- Keeping the function as small as possible to enable inlining
local records
local columns
local row
local function fetch(id): any
local tr = records[id]
if not tr then
return nil
end
return columns[tr.column][row]
end
function world_get(world: World, entity: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any
local record = world.entityIndex.sparse[entity]
if not record then
return nil
end
local archetype = record.archetype
if not archetype then
return nil
end
records = archetype.records
columns = archetype.columns
row = record.row
local va = fetch(a)
if not b then
return va
elseif not c then
return va, fetch(b)
elseif not d then
return va, fetch(b), fetch(c)
elseif not e then
return va, fetch(b), fetch(c), fetch(d)
else
error("args exceeded")
end
end
end
local function world_has(world: World, entity: number, ...: i53): boolean
local record = world.entityIndex.sparse[entity]
if not record then
return false
end
local archetype = record.archetype
if not archetype then
return false
end
local records = archetype.records
for i = 1, select("#", ...) do
if not records[select(i, ...)] then
return false
end
end
return true
end
type Item = () -> (number, ...any)
export type Query = typeof({
__iter = function(): Item
return function()
return 1
end
end,
}) & {
next: Item,
without: (Query) -> Query,
replace: (Query, (...any) -> (...any)) -> ()
}
type CompatibleArchetype = { archetype: Archetype, indices: { number } } type CompatibleArchetype = { archetype: Archetype, indices: { number } }
@ -709,22 +648,22 @@ local noop: Item = function()
return nil :: any return nil :: any
end end
local Arm = function(self: Query, ...) local Arm = function(self, ...)
return self return self
end end
local world_query local world_query
do do
local empty_list = {} local empty_list = {}
local EmptyQuery: Query = { local EmptyQuery = {
__iter = function(): Item __iter = function()
return noop return noop
end, end,
iter = function(): Item iter = function()
return noop return noop
end, end,
drain = Arm, drain = Arm,
next = noop :: Item, next = noop,
replace = noop :: (Query, ...any) -> (), replace = noop,
with = Arm, with = Arm,
without = Arm, without = Arm,
archetypes = function() archetypes = function()
@ -1208,145 +1147,6 @@ do
end end
end end
-- __nominal_type_dont_use could not be any or T as it causes a type error
-- or produces a union
export type Entity<T = nil> = number & { __DO_NOT_USE_OR_YOU_WILL_BE_FIRED: T }
export type Pair = number
export type QueryShim<T...> = typeof(setmetatable({
without = noop :: (QueryShim<T...>, ...i53) -> QueryShim<T...>,
with = noop :: (QueryShim<T...>, ...i53) -> QueryShim<T...>,
drain = noop :: (QueryShim<T...>) -> QueryShim<T...>,
replace = noop :: (QueryShim<T...>, fn: (T...) -> T...) -> (),
iter = function(self: QueryShim<T...>?): () -> (number, T...)
return noop
end
}, {
__iter = function(): () -> (number, T...)
return nil :: any
end,
}))
export type WorldShim = typeof(setmetatable(
{} :: {
--- Creates a new entity
entity: (WorldShim) -> Entity,
--- Creates a new entity located in the first 256 ids.
--- These should be used for static components for fast access.
component: <T>(WorldShim) -> Entity<T>,
--- Gets the target of an relationship. For example, when a user calls
--- `world:target(id, ChildOf(parent))`, you will obtain the parent entity.
target: (WorldShim, id: Entity, relation: Entity) -> Entity?,
--- Deletes an entity and all it's related components and relationships.
delete: (WorldShim, id: Entity) -> (),
--- Adds a component to the entity with no value
add: <T>(WorldShim, id: Entity, component: Entity<T>) -> (),
--- Assigns a value to a component on the given entity
set: <T>(WorldShim, id: Entity, component: Entity<T>, data: T) -> (),
-- Clears an entity from the world
clear: (WorldShim, id: Entity) -> (),
--- Removes a component from the given entity
remove: (WorldShim, id: Entity, component: Entity) -> (),
--- Retrieves the value of up to 4 components. These values may be nil.
get: (<A>(WorldShim, id: Entity, Entity<A>) -> A)
& (<A, B>(WorldShim, id: Entity, Entity<A>, Entity<B>) -> (A, B))
& (<A, B, C>(WorldShim, id: Entity, Entity<A>, Entity<B>, Entity<C>) -> (A, B, C))
& <A, B, C, D>(WorldShim, id: Entity, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> (A, B, C, D),
has: (WorldShim, Entity, ...Entity) -> boolean,
--- Searches the world for entities that match a given query
query: (<A>(WorldShim, Entity<A>) -> QueryShim<A>)
& (<A, B>(WorldShim, Entity<A>, Entity<B>) -> QueryShim<A, B>)
& (<A, B, C>(WorldShim, Entity<A>, Entity<B>, Entity<C>) -> QueryShim<A, B, C>)
& (<A, B, C, D>(WorldShim, Entity<A>, Entity<B>, Entity<C>, Entity<D>) -> QueryShim<A, B, C, D>)
& (<A, B, C, D, E>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>
) -> QueryShim<A, B, C, D, E>)
& (<A, B, C, D, E, F>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>
) -> QueryShim<A, B, C, D, E, F>)
& (<A, B, C, D, E, F, G>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>,
Entity<G>
) -> QueryShim<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>,
Entity<G>,
Entity<H>
) -> QueryShim<A, B, C, D, E, F, G, H>)
& (<A, B, C, D, E, F, G, H, I>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>,
Entity<G>,
Entity<H>,
Entity<I>
) -> QueryShim<A, B, C, D, E, F, G, H, I>)
& (<A, B, C, D, E, F, G, H, I, J>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>,
Entity<G>,
Entity<H>,
Entity<I>,
Entity<J>
) -> QueryShim<A, B, C, D, E, F, G, H, I, J>)
& (<A, B, C, D, E, F, G, H, I, J, K>(
WorldShim,
Entity<A>,
Entity<B>,
Entity<C>,
Entity<D>,
Entity<E>,
Entity<F>,
Entity<G>,
Entity<H>,
Entity<I>,
Entity<J>,
Entity<K>,
...Entity<any>
) -> QueryShim<A, B, C, D, E, F, G, H, I, J, K>),
},
{} :: {
__iter: (world: WorldShim) -> () -> (number, { [unknown]: unknown? }),
}
))
local World = {} local World = {}
World.__index = World World.__index = World
@ -1391,8 +1191,64 @@ function World.new()
return self return self
end end
export type Pair = number
type Item = () -> (number, ...any)
export type Entity<T = nil> = number & {__DO_NOT_USE_OR_YOU_WILL_BE_FIRED: T }
type Iter<T...> = () -> () -> (Entity, T...)
type Query<T...> = typeof(setmetatable({}, {
__iter = (nil :: any) :: Iter<T...>
})) & {
iter: Iter<T...>,
next: Item,
with: (Query<T...>) -> Query<T...>,
without: (Query<T...>, ...i53) -> Query<T...>,
replace: (Query<T...>, <U...>(T...) -> (U...)) -> (),
archetypes: () -> { Archetype },
}
export type World = {
archetypeIndex: { [string]: Archetype },
archetypes: Archetypes,
componentIndex: ComponentIndex,
entityIndex: EntityIndex,
nextArchetypeId: number,
nextComponentId: number,
nextEntityId: number,
ROOT_ARCHETYPE: Archetype,
} & {
target: (world: World, entity: Entity, relation: Entity) -> Entity,
parent: (world: World, entity: Entity) -> Entity,
entity: (world: World) -> Entity,
clear: (world: World, entity: Entity) -> (),
delete: (world: World, entity: Entity) -> (),
component: <T>(world: World) -> Entity<T>,
get: <T>(world: World, entity: Entity, id: Entity<T>) -> T,
has: (world: World, entity: Entity, id: Entity) -> boolean,
add: (world: World, entity: Entity, id: Entity) -> (),
set: <T>(world: World, entity: Entity,
id: Entity<T>, data: T) -> (),
remove: (world: World, entity: Entity, id: Entity) -> (),
query:
(<A>(World, Entity<A>) -> Query<A>)
& (<A, B>(World, Entity<A>, Entity<B>) -> Query<A, B>)
& (<A, B, C>(World, Entity<A>, Entity<B>, Entity<C>) -> Query<A, B, C>)
& (<A, B, C, D>(World, Entity<A>, Entity<B>, Entity<C>,
Entity<D>) -> Query<A, B, C, D>)
& (<A, B, C, D, E>(World, Entity<A>, Entity<B>, Entity<C>,
Entity<D>, Entity<E>) -> Query<A, B, C, D, E>)
& (<A, B, C, D, E, F>(World, Entity<A>, Entity<B>, Entity<C>,
Entity<D>, Entity<E>, Entity<F>) -> Query<A, B, C, D, E, F>)
& (<A, B, C, D, E, F, G>(World, Entity<A>, Entity<B>, Entity<C>,
Entity<D>, Entity<E>, Entity<F>, Entity<G>) -> Query<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>(World, Entity<A>, Entity<B>, Entity<C>,
Entity<D>, Entity<E>, Entity<F>, Entity<G>, Entity<H>) -> Query<A, B, C, D, E, F, G, H>)
}
return { return {
World = World :: { new: () -> WorldShim } , World = World ,
OnAdd = EcsOnAdd :: Entity, OnAdd = EcsOnAdd :: Entity,
OnRemove = EcsOnRemove :: Entity, OnRemove = EcsOnRemove :: Entity,

View file

@ -697,22 +697,6 @@ 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"
local world = jecs.World.new()
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, C)
world:add(e, D)
CHECK(world:has(e, A, B, C, D) == false)
end
end) end)
TEST("world:component()", function() TEST("world:component()", function()