mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Remove multret (#99)
* Initial commit * Move functions for get and has * Update docs
This commit is contained in:
parent
a2dedf128a
commit
e6983a3356
3 changed files with 109 additions and 260 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
334
src/init.luau
334
src/init.luau
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue