From e6983a3356fcedc2940edd2675f909f86558f4ec Mon Sep 17 00:00:00 2001 From: Marcus Date: Sun, 11 Aug 2024 03:27:29 +0200 Subject: [PATCH] Remove multret (#99) * Initial commit * Move functions for get and has * Update docs --- docs/api/world.md | 19 ++- src/init.luau | 334 +++++++++++++--------------------------------- test/tests.luau | 16 --- 3 files changed, 109 insertions(+), 260 deletions(-) diff --git a/docs/api/world.md b/docs/api/world.md index 8e3bb3e..cbedf2e 100644 --- a/docs/api/world.md +++ b/docs/api/world.md @@ -70,12 +70,21 @@ For example, a Health type should be created using this. ### get() ```luau -function World:get( +function World:get( entity: Entity, -- The entity - ...: Entity -- The types to fetch -): ... -- Returns the component data in the same order they were passed in + id: Entity -- The component ID to fetch +): 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 -- 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 function World:query( ...: 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. diff --git a/src/init.luau b/src/init.luau index baf1553..d8d3720 100644 --- a/src/init.luau +++ b/src/init.luau @@ -56,17 +56,6 @@ type ArchetypeDiff = { 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 EcsOnAdd = HI_COMPONENT_ID + 1 @@ -375,6 +364,38 @@ local function world_parent(world: World, entity: i53) return world_target(world, entity, EcsChildOf) 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 if #types < 1 then return world.ROOT_ARCHETYPE @@ -620,88 +641,6 @@ local function world_clear(world: World, entity: i53) entity_move(world.entityIndex, entity, record, ROOT_ARCHETYPE) 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 } } @@ -709,22 +648,22 @@ local noop: Item = function() return nil :: any end -local Arm = function(self: Query, ...) +local Arm = function(self, ...) return self end local world_query do local empty_list = {} - local EmptyQuery: Query = { - __iter = function(): Item + local EmptyQuery = { + __iter = function() return noop end, - iter = function(): Item + iter = function() return noop end, drain = Arm, - next = noop :: Item, - replace = noop :: (Query, ...any) -> (), + next = noop, + replace = noop, with = Arm, without = Arm, archetypes = function() @@ -1208,145 +1147,6 @@ do 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 = number & { __DO_NOT_USE_OR_YOU_WILL_BE_FIRED: T } -export type Pair = number - -export type QueryShim = typeof(setmetatable({ - without = noop :: (QueryShim, ...i53) -> QueryShim, - with = noop :: (QueryShim, ...i53) -> QueryShim, - drain = noop :: (QueryShim) -> QueryShim, - replace = noop :: (QueryShim, fn: (T...) -> T...) -> (), - iter = function(self: QueryShim?): () -> (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: (WorldShim) -> Entity, - --- 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: (WorldShim, id: Entity, component: Entity) -> (), - --- Assigns a value to a component on the given entity - set: (WorldShim, id: Entity, component: Entity, 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: ((WorldShim, id: Entity, Entity) -> A) - & ((WorldShim, id: Entity, Entity, Entity) -> (A, B)) - & ((WorldShim, id: Entity, Entity, Entity, Entity) -> (A, B, C)) - & (WorldShim, id: Entity, Entity, Entity, Entity, Entity) -> (A, B, C, D), - - has: (WorldShim, Entity, ...Entity) -> boolean, - - --- Searches the world for entities that match a given query - query: ((WorldShim, Entity) -> QueryShim) - & ((WorldShim, Entity, Entity) -> QueryShim) - & ((WorldShim, Entity, Entity, Entity) -> QueryShim) - & ((WorldShim, Entity, Entity, Entity, Entity) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity - ) -> QueryShim) - & (( - WorldShim, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - Entity, - ...Entity - ) -> QueryShim), - }, - {} :: { - __iter: (world: WorldShim) -> () -> (number, { [unknown]: unknown? }), - } -)) - local World = {} World.__index = World @@ -1391,8 +1191,64 @@ function World.new() return self end +export type Pair = number + +type Item = () -> (number, ...any) + +export type Entity = number & {__DO_NOT_USE_OR_YOU_WILL_BE_FIRED: T } + +type Iter = () -> () -> (Entity, T...) + +type Query = typeof(setmetatable({}, { + __iter = (nil :: any) :: Iter +})) & { + iter: Iter, + next: Item, + with: (Query) -> Query, + without: (Query, ...i53) -> Query, + replace: (Query, (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: (world: World) -> Entity, + get: (world: World, entity: Entity, id: Entity) -> T, + has: (world: World, entity: Entity, id: Entity) -> boolean, + add: (world: World, entity: Entity, id: Entity) -> (), + set: (world: World, entity: Entity, + id: Entity, data: T) -> (), + remove: (world: World, entity: Entity, id: Entity) -> (), + query: + ((World, Entity) -> Query) + & ((World, Entity, Entity) -> Query) + & ((World, Entity, Entity, Entity) -> Query) + & ((World, Entity, Entity, Entity, + Entity) -> Query) + & ((World, Entity, Entity, Entity, + Entity, Entity) -> Query) + & ((World, Entity, Entity, Entity, + Entity, Entity, Entity) -> Query) + & ((World, Entity, Entity, Entity, + Entity, Entity, Entity, Entity) -> Query) + & ((World, Entity, Entity, Entity, + Entity, Entity, Entity, Entity, Entity) -> Query) +} return { - World = World :: { new: () -> WorldShim } , + World = World , OnAdd = EcsOnAdd :: Entity, OnRemove = EcsOnRemove :: Entity, diff --git a/test/tests.luau b/test/tests.luau index 154505b..27aedc0 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -697,22 +697,6 @@ TEST("world:has()", function() CHECK(world:has(e, Tag)) 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) TEST("world:component()", function()