From 303bd4ed165395d632c1155543e998c6913cdcbc Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 13 Nov 2024 20:05:01 +0100 Subject: [PATCH 01/20] Entity recycling (#156) * Merge * Initial commit * Fix indentations * Remove sparse_count * Add whiteline * return 0 instead * Add check for friend existing * Force inlining * Manual inlining --- src/init.luau | 218 +++++++++++++++++++++++++++++++----------------- test/tests.luau | 15 ++-- 2 files changed, 151 insertions(+), 82 deletions(-) diff --git a/src/init.luau b/src/init.luau index 7cb467d..35c8d72 100644 --- a/src/init.luau +++ b/src/init.luau @@ -44,11 +44,6 @@ type Record = { dense: i24, } -type EntityIndex = { - dense: Map, - sparse: Map, -} - type ArchetypeRecord = { count: number, column: number, @@ -74,6 +69,13 @@ type ArchetypeDiff = { removed: Ty, } +type EntityIndex = { + dense_array: Map, + sparse_array: Map, + alive_count: number, + max_id: number, +} + local HI_COMPONENT_ID = _G.__JECS_HI_COMPONENT_ID or 256 -- stylua: ignore start local EcsOnAdd = HI_COMPONENT_ID + 1 @@ -141,7 +143,12 @@ local function ECS_GENERATION_INC(e: i53) local id = flags // ECS_ENTITY_MASK local generation = flags % ECS_GENERATION_MASK - return ECS_COMBINE(id, generation + 1) + flags + local next_gen = generation + 1 + if next_gen > ECS_GENERATION_MASK then + return id + end + + return ECS_COMBINE(id, next_gen) + flags end return ECS_COMBINE(e, 1) end @@ -164,49 +171,73 @@ local function ECS_PAIR(pred: i53, obj: i53): i53 return ECS_COMBINE(ECS_ENTITY_T_LO(obj), ECS_ENTITY_T_LO(pred)) + FLAGS_ADD(--[[isPair]] true) :: i53 end -local ERROR_ENTITY_NOT_ALIVE = "Entity is not alive" -local ERROR_GENERATION_INVALID = "INVALID GENERATION" - -local function entity_index_get_alive(index: EntityIndex, e: i24): i53 - local denseArray = index.dense - local id = denseArray[ECS_ENTITY_T_LO(e)] - - if id then - local currentGeneration = ECS_GENERATION(id) - local gen = ECS_GENERATION(e) - if gen == currentGeneration then - return id - end - - error(ERROR_GENERATION_INVALID) +local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? + local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] + if not r then + return nil end - error(ERROR_ENTITY_NOT_ALIVE) + if not r or r.dense == 0 then + return nil + end + + return r end -local function _entity_index_sparse_get(entityIndex, id) - return entityIndex.sparse[entity_index_get_alive(entityIndex, id)] +local function entity_index_try_get(entity_index: EntityIndex, entity: number): Record? + local r = entity_index_try_get_any(entity_index, entity) + if r then + local r_dense = r.dense + if r_dense > entity_index.alive_count then + return nil + end + if entity_index.dense_array[r_dense] ~= entity then + return nil + end + end + return r +end + +local function entity_index_get_alive(index: EntityIndex, e: i24): i53 + local r = entity_index_try_get_any(index, e) + if r then + return index.dense_array[r.dense] + end + return 0 +end + +local function entity_index_is_alive(entity_index: EntityIndex, entity: number) + return entity_index_try_get(entity_index, entity) ~= nil +end + +local function entity_index_new_id(entity_index: EntityIndex, data): i53 + local dense_array = entity_index.dense_array + local alive_count = entity_index.alive_count + if alive_count ~= #dense_array then + alive_count += 1 + entity_index.alive_count = alive_count + local id = dense_array[alive_count] + return id + end + + local id = entity_index.max_id + 1 + entity_index.max_id = id + alive_count += 1 + entity_index.alive_count = alive_count + dense_array[alive_count] = id + entity_index.sparse_array[id] = { dense = alive_count } :: Record + + return id end -- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits local function ecs_pair_first(world, e) - return entity_index_get_alive(world.entityIndex, ECS_ENTITY_T_HI(e)) + return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e)) end -- ECS_PAIR_SECOND gets the relationship / pred / LOW bits local function ecs_pair_second(world, e) - return entity_index_get_alive(world.entityIndex, ECS_ENTITY_T_LO(e)) -end - -local function entity_index_new_id(entityIndex: EntityIndex, index: i24): i53 - --local id = ECS_COMBINE(index, 0) - local id = index - entityIndex.sparse[id] = { - dense = index, - } :: Record - entityIndex.dense[index] = id - - return id + return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e)) end local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: i24, from: Archetype, src_row: i24) @@ -239,7 +270,6 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: column[last] = nil end - local sparse = entity_index.sparse local moved = #src_entities -- Move the entity from the source to the destination archetype. @@ -255,8 +285,8 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: src_entities[moved] = nil :: any dst_entities[dst_row] = e1 - local record1 = sparse[e1] - local record2 = sparse[e2] + local record1 = entity_index_try_get_any(entity_index, e1) + local record2 = entity_index_try_get_any(entity_index, e2) record1.row = dst_row record2.row = src_row @@ -307,7 +337,7 @@ do 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] + local record = entity_index_try_get(world.entity_index, entity) if not record then return nil end @@ -338,7 +368,7 @@ do end local function world_get_one_inline(world: World, entity: i53, id: i53): any - local record = world.entityIndex.sparse[entity] + local record = entity_index_try_get(world.entity_index, entity) if not record then return nil end @@ -356,7 +386,7 @@ local function world_get_one_inline(world: World, entity: i53, id: i53): any end local function world_has_one_inline(world: World, entity: number, id: i53): boolean - local record = world.entityIndex.sparse[entity] + local record = entity_index_try_get(world.entity_index, entity) if not record then return false end @@ -372,7 +402,7 @@ local function world_has_one_inline(world: World, entity: number, id: i53): bool end local function world_has(world: World, entity: number, ...: i53): boolean - local record = world.entityIndex.sparse[entity] + local record = entity_index_try_get(world.entity_index, entity) if not record then return false end @@ -395,7 +425,11 @@ end local function world_target(world: World, entity: i53, relation: i24, index: number?): i24? local nth = index or 0 - local record = world.entityIndex.sparse[entity] + local record = entity_index_try_get(world.entity_index, entity) + if not record then + return nil + end + local archetype = record.archetype if not archetype then return nil @@ -543,9 +577,7 @@ local function archetype_create(world: World, types: { i24 }, ty, prev: i53?): A end local function world_entity(world: World): i53 - local entityId = (world.nextEntityId :: number) + 1 - world.nextEntityId = entityId - return entity_index_new_id(world.entityIndex, entityId + EcsRest) + return entity_index_new_id(world.entity_index) end local function world_parent(world: World, entity: i53) @@ -701,15 +733,19 @@ local function invoke_hook(action, entity, data) end local function world_add(world: World, entity: i53, id: i53): () - local entityIndex = world.entityIndex - local record = entityIndex.sparse[entity] + local entity_index = world.entity_index + local record = entity_index_try_get(entity_index, entity) + if not record then + return + end + local from = record.archetype local to = archetype_traverse_add(world, id, from) if from == to then return end if from then - entity_move(entityIndex, entity, record, to) + entity_move(entity_index, entity, record, to) else if #to.types > 0 then new_entity(entity, record, to) @@ -725,8 +761,12 @@ local function world_add(world: World, entity: i53, id: i53): () end local function world_set(world: World, entity: i53, id: i53, data: unknown): () - local entityIndex = world.entityIndex - local record = entityIndex.sparse[entity] + local entity_index = world.entity_index + local record = entity_index_try_get(entity_index, entity) + if not record then + return + end + local from: Archetype = record.archetype local to: Archetype = archetype_traverse_add(world, id, from) local idr = world.componentIndex[id] @@ -741,7 +781,8 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown): () -- If the archetypes are the same it can avoid moving the entity -- and just set the data directly. local tr = to.records[id] - from.columns[tr.column][record.row] = data + local column = from.columns[tr.column] + column[record.row] = data local on_set = idr_hooks.on_set if on_set then on_set(entity, data) @@ -752,7 +793,7 @@ local function world_set(world: World, entity: i53, id: i53, data: unknown): () if from then -- If there was a previous archetype, then the entity needs to move the archetype - entity_move(entityIndex, entity, record, to) + entity_move(entity_index, entity, record, to) else if #to.types > 0 then -- When there is no previous archetype it should create the archetype @@ -788,15 +829,18 @@ local function world_component(world: World): i53 error("Too many components, consider using world:entity() instead to create components.") end world.nextComponentId = componentId - local id = entity_index_new_id(world.entityIndex, componentId) - world_add(world, id, EcsComponent) - return id + + return componentId end local function world_remove(world: World, entity: i53, id: i53) - local entity_index = world.entityIndex - local record = entity_index.sparse[entity] + local entity_index = world.entity_index + local record = entity_index_try_get(entity_index, entity) + if not record then + return + end local from = record.archetype + if not from then return end @@ -831,7 +875,7 @@ local function archetype_fast_delete(columns: { Column }, column_count: number, end local function archetype_delete(world: World, archetype: Archetype, row: number, destruct: boolean?) - local entityIndex = world.entityIndex + local entityIndex = world.entity_index local columns = archetype.columns local types = archetype.types local entities = archetype.entities @@ -844,7 +888,7 @@ local function archetype_delete(world: World, archetype: Archetype, row: number, if row ~= last then -- TODO: should be "entity_index_sparse_get(entityIndex, move)" - local record_to_move = entityIndex.sparse[move] + local record_to_move = entity_index_try_get_any(entityIndex, move) if record_to_move then record_to_move.row = row end @@ -868,7 +912,7 @@ end local function world_clear(world: World, entity: i53) --TODO: use sparse_get (stashed) - local record = world.entityIndex.sparse[entity] + local record = entity_index_try_get(world.entity_index, entity) if not record then return end @@ -983,10 +1027,8 @@ end local world_delete: (world: World, entity: i53, destruct: boolean?) -> () do function world_delete(world: World, entity: i53, destruct: boolean?) - local entityIndex = world.entityIndex - local sparse_array = entityIndex.sparse - - local record = sparse_array[entity] + local entity_index = world.entity_index + local record = entity_index_try_get(entity_index, entity) if not record then return end @@ -1044,7 +1086,7 @@ do if not ECS_IS_PAIR(id) then continue end - local object = ECS_ENTITY_T_LO(id) + local object = ecs_pair_second(world, id) if object == delete then local id_record = component_index[id] local flags = id_record.flags @@ -1067,13 +1109,25 @@ do end end + local dense_array = entity_index.dense_array + local index_of_deleted_entity = record.dense + local index_of_last_alive_entity = entity_index.alive_count + entity_index.alive_count = index_of_last_alive_entity - 1 + + local last_alive_entity = dense_array[index_of_last_alive_entity] + local r_swap = entity_index_try_get_any(entity_index, last_alive_entity) :: Record + r_swap.dense = index_of_deleted_entity record.archetype = nil :: any - sparse_array[entity] = nil :: any + record.row = nil :: any + record.dense = index_of_last_alive_entity + + dense_array[index_of_deleted_entity] = last_alive_entity + dense_array[index_of_last_alive_entity] = ECS_GENERATION_INC(entity) end end local function world_contains(world: World, entity): boolean - return world.entityIndex.sparse[entity] ~= nil + return entity_index_is_alive(world.entity_index, entity) end local function NOOP() end @@ -1609,14 +1663,17 @@ if _G.__JECS_DEBUG then end function World.new() + local entity_index: EntityIndex = { + dense_array = {} :: { [i24]: i53 }, + sparse_array = {} :: { [i53]: Record }, + alive_count = 0, + max_id = 0, + } local self = setmetatable({ archetypeIndex = {} :: { [string]: Archetype }, archetypes = {} :: Archetypes, componentIndex = {} :: ComponentIndex, - entityIndex = { - dense = {} :: { [i24]: i53 }, - sparse = {} :: { [i53]: Record }, - } :: EntityIndex, + entity_index = entity_index, nextArchetypeId = 0 :: number, nextComponentId = 0 :: number, nextEntityId = 0 :: number, @@ -1625,9 +1682,14 @@ function World.new() self.ROOT_ARCHETYPE = archetype_create(self, {}, "") + for i = 1, HI_COMPONENT_ID do + local e = entity_index_new_id(entity_index, i) + world_add(self, e, EcsComponent) + end + for i = HI_COMPONENT_ID + 1, EcsRest do -- Initialize built-in components - entity_index_new_id(self.entityIndex, i) + entity_index_new_id(entity_index, i) end world_add(self, EcsName, EcsComponent) @@ -1692,7 +1754,7 @@ export type World = { archetypeIndex: { [string]: Archetype }, archetypes: Archetypes, componentIndex: ComponentIndex, - entityIndex: EntityIndex, + entity_index: EntityIndex, ROOT_ARCHETYPE: Archetype, nextComponentId: number, @@ -1819,4 +1881,8 @@ return { create_edge_for_remove = create_edge_for_remove, archetype_traverse_add = archetype_traverse_add, archetype_traverse_remove = archetype_traverse_remove, + + entity_index_try_get = entity_index_try_get, + entity_index_try_get_any = entity_index_try_get_any, + entity_index_is_alive = entity_index_is_alive, } diff --git a/test/tests.luau b/test/tests.luau index b9375ef..ee02770 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -7,9 +7,11 @@ local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC local IS_PAIR = jecs.IS_PAIR local pair = jecs.pair -local getAlive = jecs.entity_index_get_alive local ecs_pair_first = jecs.pair_first local ecs_pair_second = jecs.pair_second +local entity_index_try_get_any = jecs.entity_index_try_get_any +local entity_index_get_alive = jecs.entity_index_get_alive +local entity_index_is_alive = jecs.entity_index_is_alive local world_new = jecs.World.new local TEST, CASE, CHECK, FINISH, SKIP, FOCUS = testkit.test() @@ -29,7 +31,7 @@ type World = jecs.WorldShim local function debug_world_inspect(world) local function record(e) - return world.entityIndex.sparse[e] + return entity_index_try_get_any(world.entity_index, e) end local function tbl(e) return record(e).archetype @@ -168,7 +170,6 @@ TEST("world:entity()", function() local world = jecs.World.new() local e = world:entity() CHECK(ECS_ID(e) == 1 + jecs.Rest) - CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e) CHECK(ECS_GENERATION(e) == 0) -- 0 e = ECS_GENERATION_INC(e) CHECK(ECS_GENERATION(e) == 1) -- 1 @@ -878,9 +879,10 @@ TEST("world:clear()", function() CHECK(archetype_entities[1] == _e) CHECK(archetype_entities[2] == _e1) - local sparse_array = world.entityIndex.sparse - local e_record = sparse_array[e] - local e1_record = sparse_array[e1] + local e_record = entity_index_try_get_any( + world.entity_index, e) + local e1_record = entity_index_try_get_any( + world.entity_index, e1) CHECK(e_record.archetype == archetype) CHECK(e1_record.archetype == archetype) CHECK(e1_record.row == 2) @@ -1085,6 +1087,7 @@ TEST("world:delete", function() for i, friend in friends do CHECK(not world:has(friend, pair(FriendsWith, e))) CHECK(world:has(friend, Health)) + CHECK(world:contains(friend)) end end From ceb7ea9866f1fa8576bbfe8de01751ff42354357 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 02:22:28 +0100 Subject: [PATCH 02/20] Fix typings of exported pair function --- src/init.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.luau b/src/init.luau index 35c8d72..1eb67d2 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1853,7 +1853,7 @@ return { Name = EcsName :: Entity, Rest = EcsRest :: Entity, - pair = ECS_PAIR, + pair = ECS_PAIR :: (first: P, second: O) -> Pair, -- Inwards facing API for testing ECS_ID = ECS_ENTITY_T_LO, From b40af9fe9daf49da108ed49aee33a95554964d3b Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 03:38:27 +0100 Subject: [PATCH 03/20] Fix generation increment overflowing id --- src/init.luau | 8 +- test/gen.luau | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ test/lol.luau | 157 +++++++++++++++++++++++++++++++++++++++ test/tests.luau | 30 ++++++++ 4 files changed, 384 insertions(+), 3 deletions(-) create mode 100644 test/gen.luau create mode 100644 test/lol.luau diff --git a/src/init.luau b/src/init.luau index 1eb67d2..a97d369 100644 --- a/src/init.luau +++ b/src/init.luau @@ -148,7 +148,7 @@ local function ECS_GENERATION_INC(e: i53) return id end - return ECS_COMBINE(id, next_gen) + flags + return ECS_COMBINE(id, next_gen) end return ECS_COMBINE(e, 1) end @@ -1683,13 +1683,13 @@ function World.new() self.ROOT_ARCHETYPE = archetype_create(self, {}, "") for i = 1, HI_COMPONENT_ID do - local e = entity_index_new_id(entity_index, i) + local e = entity_index_new_id(entity_index) world_add(self, e, EcsComponent) end for i = HI_COMPONENT_ID + 1, EcsRest do -- Initialize built-in components - entity_index_new_id(entity_index, i) + entity_index_new_id(entity_index) end world_add(self, EcsName, EcsComponent) @@ -1885,4 +1885,6 @@ return { entity_index_try_get = entity_index_try_get, entity_index_try_get_any = entity_index_try_get_any, entity_index_is_alive = entity_index_is_alive, + entity_index_remove = entity_index_remove, + entity_index_new_id = entity_index_new_id } diff --git a/test/gen.luau b/test/gen.luau new file mode 100644 index 0000000..19d1244 --- /dev/null +++ b/test/gen.luau @@ -0,0 +1,192 @@ +type i53 = number +type i24 = number + +type Ty = { i53 } +type ArchetypeId = number + +type Column = { any } + +type Map = { [K]: V } + +type GraphEdge = { + from: Archetype, + to: Archetype?, + prev: GraphEdge?, + next: GraphEdge?, + id: number, +} + +type GraphEdges = Map + +type GraphNode = { + add: GraphEdges, + remove: GraphEdges, + refs: GraphEdge, +} + +type ArchetypeRecord = { + count: number, + column: number, +} + +export type Archetype = { + id: number, + node: GraphNode, + types: Ty, + type: string, + entities: { number }, + columns: { Column }, + records: { ArchetypeRecord }, +} +type Record = { + archetype: Archetype, + row: number, + dense: i24, +} + +type EntityIndex = { + dense_array: Map, + sparse_array: Map, + sparse_count: number, + alive_count: number, + max_id: number +} + + +local ECS_PAIR_FLAG = 0x8 +local ECS_ID_FLAGS_MASK = 0x10 +local ECS_ENTITY_MASK = bit32.lshift(1, 24) +local ECS_GENERATION_MASK = bit32.lshift(1, 16) + +-- HIGH 24 bits LOW 24 bits +local function ECS_GENERATION(e: i53): i24 + return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0 +end + +local function ECS_COMBINE(source: number, target: number): i53 + return (source * 268435456) + (target * ECS_ID_FLAGS_MASK) +end + +local function ECS_GENERATION_INC(e: i53) + if e > ECS_ENTITY_MASK then + local flags = e // ECS_ID_FLAGS_MASK + local id = flags // ECS_ENTITY_MASK + local generation = flags % ECS_GENERATION_MASK + + local next_gen = generation + 1 + if next_gen > ECS_GENERATION_MASK then + return id + end + + return ECS_COMBINE(id, next_gen) + flags + end + return ECS_COMBINE(e, 1) +end +local function ECS_ENTITY_T_LO(e: i53): i24 + return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e +end + + +local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? + local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] + if not r then + return nil + end + + if not r or r.dense == 0 then + return nil + end + + return r +end + +local function entity_index_try_get(entity_index: EntityIndex, entity: number): Record? + local r = entity_index_try_get_any(entity_index, entity) + if r then + local r_dense = r.dense + if r_dense > entity_index.alive_count then + return nil + end + if entity_index.dense_array[r_dense] ~= entity then + return nil + end + end + return r +end + +local function entity_index_get_alive(entity_index: EntityIndex, + entity: number): number + + local r = entity_index_try_get_any(entity_index, entity) + if r then + return entity_index.dense_array[r.dense] + end + return 0 +end + +local function entity_index_remove(entity_index: EntityIndex, entity: number) + local r = entity_index_try_get(entity_index, entity) + if not r then + return + end + local dense_array = entity_index.dense_array + local index_of_deleted_entity = r.dense + local last_entity_alive_at_index = entity_index.alive_count + entity_index.alive_count -= 1 + + local last_alive_entity = dense_array[last_entity_alive_at_index] + local r_swap = entity_index_try_get_any( + entity_index, last_alive_entity) :: Record + r_swap.dense = index_of_deleted_entity + r.archetype = nil :: any + r.row = nil :: any + r.dense = last_entity_alive_at_index + + dense_array[index_of_deleted_entity] = last_alive_entity + dense_array[last_entity_alive_at_index] = ECS_GENERATION_INC(entity) +end + +local function entity_index_new_id(entity_index: EntityIndex, data): i53 + local dense_array = entity_index.dense_array + if entity_index.alive_count ~= #dense_array then + entity_index.alive_count += 1 + local id = dense_array[entity_index.alive_count] + return id + end + entity_index.max_id +=1 + local id = entity_index.max_id + entity_index.alive_count += 1 + + dense_array[entity_index.alive_count] = id + entity_index.sparse_array[id] = { + dense = entity_index.alive_count, + archetype = data + } :: Record + + entity_index.sparse_count += 1 + + return id +end + +local function entity_index_is_alive(entity_index: EntityIndex, entity: number) + return entity_index_try_get(entity_index, entity) ~= nil +end + +local eidx = { + alive_count = 0, + max_id = 0, + sparse_array = {} :: { Record }, + sparse_count = 0, + dense_array = {} :: { i53 } +} +local e1v0 = entity_index_new_id(eidx, "e1v0") +local e2v0 = entity_index_new_id(eidx, "e2v0") +local e3v0 = entity_index_new_id(eidx, "e3v0") +local e4v0 = entity_index_new_id(eidx, "e4v0") +local e5v0 = entity_index_new_id(eidx, "e5v0") +local t = require("@testkit") +local tprint = t.print +entity_index_remove(eidx, e5v0) +local e5v1 = entity_index_new_id(eidx, "e5v1") +entity_index_remove(eidx, e2v0) +tprint(eidx) diff --git a/test/lol.luau b/test/lol.luau new file mode 100644 index 0000000..cbf50dd --- /dev/null +++ b/test/lol.luau @@ -0,0 +1,157 @@ +local c = { + white_underline = function(s: any) + return `\27[1;4m{s}\27[0m` + end, + + white = function(s: any) + return `\27[37;1m{s}\27[0m` + end, + + green = function(s: any) + return `\27[32;1m{s}\27[0m` + end, + + red = function(s: any) + return `\27[31;1m{s}\27[0m` + end, + + yellow = function(s: any) + return `\27[33;1m{s}\27[0m` + end, + + red_highlight = function(s: any) + return `\27[41;1;30m{s}\27[0m` + end, + + green_highlight = function(s: any) + return `\27[42;1;30m{s}\27[0m` + end, + + gray = function(s: any) + return `\27[30;1m{s}\27[0m` + end, +} + + +local ECS_PAIR_FLAG = 0x8 +local ECS_ID_FLAGS_MASK = 0x10 +local ECS_ENTITY_MASK = bit32.lshift(1, 24) +local ECS_GENERATION_MASK = bit32.lshift(1, 16) + +type i53 = number +type i24 = number + +local function ECS_ENTITY_T_LO(e: i53): i24 + return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e +end + +local function ECS_GENERATION(e: i53): i24 + return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0 +end + +local ECS_ID = ECS_ENTITY_T_LO + +local function ECS_COMBINE(source: number, target: number): i53 + return (source * 268435456) + (target * ECS_ID_FLAGS_MASK) +end + +local function ECS_GENERATION_INC(e: i53) + if e > ECS_ENTITY_MASK then + local flags = e // ECS_ID_FLAGS_MASK + local id = flags // ECS_ENTITY_MASK + local generation = flags % ECS_GENERATION_MASK + + local next_gen = generation + 1 + if next_gen > ECS_GENERATION_MASK then + return id + end + + return ECS_COMBINE(id, next_gen) + flags + end + return ECS_COMBINE(e, 1) +end + +local function bl() + print("") +end + +local function pe(e) + local gen = ECS_GENERATION(e) + return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`) +end + +local function dprint(tbl: { [number]: number }) + bl() + print("--------") + for i, e in tbl do + print("| "..pe(e).." |") + print("--------") + end + bl() +end + +local max_id = 0 +local alive_count = 0 +local dense = {} +local sparse = {} +local function alloc() + if alive_count ~= #dense then + alive_count += 1 + print("*recycled", pe(dense[alive_count])) + return dense[alive_count] + end + max_id += 1 + local id = max_id + alive_count += 1 + dense[alive_count] = id + sparse[id] = { + dense = alive_count + } + print("*allocated", pe(id)) + return id +end + +local function remove(entity) + local id = ECS_ID(entity) + local r = sparse[id] + local index_of_deleted_entity = r.dense + local last_entity_alive_at_index = alive_count -- last entity alive + alive_count -= 1 + local last_alive_entity = dense[last_entity_alive_at_index] + local r_swap = sparse[ECS_ID(last_alive_entity)] + r_swap.dense = r.dense + r.dense = last_entity_alive_at_index + dense[index_of_deleted_entity] = last_alive_entity + dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity) +end + +local function alive(e) + local r = sparse[ECS_ID(e)] + + return dense[r.dense] == e +end + +local function pa(e) + print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`) +end + +local tprint = require("@testkit").print +local e1v0 = alloc() +local e2v0 = alloc() +local e3v0 = alloc() +local e4v0 = alloc() +local e5v0 = alloc() +pa(e1v0) +pa(e4v0) +remove(e5v0) +pa(e5v0) + +local e5v1 = alloc() +pa(e5v0) +pa(e5v1) +pa(e2v0) +print(ECS_ID(e2v0)) + +dprint(dense) +remove(e2v0) +dprint(dense) diff --git a/test/tests.luau b/test/tests.luau index ee02770..efa86cd 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -191,6 +191,36 @@ TEST("world:entity()", function() CHECK(ecs_pair_first(world, pair) == e2) CHECK(ecs_pair_second(world, pair) == e3) end + + do CASE "Recycling" + local world = world_new() + local e = world:entity() + world:delete(e) + local e1 = world:entity() + world:delete(e1) + local e2 = world:entity() + CHECK(ECS_ID(e2) == e) + CHECK(ECS_GENERATION(e2) == 2) + CHECK(world:contains(e2)) + CHECK(not world:contains(e1)) + CHECK(not world:contains(e)) + end + + do CASE "Recycling max generation" + local world = world_new() + local pin = jecs.Rest + 1 + for i = 1, 2^16-1 do + local e = world:entity() + world:delete(e) + end + local e = world:entity() + CHECK(ECS_ID(e) == pin) + CHECK(ECS_GENERATION(e) == 2^16-1) + world:delete(e) + e = world:entity() + CHECK(ECS_ID(e) == pin) + CHECK(ECS_GENERATION(e) == 0) + end end) TEST("world:set()", function() From 906661137397b96837291dd448b9b410f2e4d499 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 03:39:46 +0100 Subject: [PATCH 04/20] Fix ECS_GENERATION_INC --- test/gen.luau | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/test/gen.luau b/test/gen.luau index 19d1244..3b5599c 100644 --- a/test/gen.luau +++ b/test/gen.luau @@ -72,13 +72,8 @@ local function ECS_GENERATION_INC(e: i53) local flags = e // ECS_ID_FLAGS_MASK local id = flags // ECS_ENTITY_MASK local generation = flags % ECS_GENERATION_MASK - - local next_gen = generation + 1 - if next_gen > ECS_GENERATION_MASK then - return id - end - - return ECS_COMBINE(id, next_gen) + flags + print(generation) + return ECS_COMBINE(id, generation + 1) end return ECS_COMBINE(e, 1) end @@ -146,25 +141,23 @@ local function entity_index_remove(entity_index: EntityIndex, entity: number) dense_array[last_entity_alive_at_index] = ECS_GENERATION_INC(entity) end -local function entity_index_new_id(entity_index: EntityIndex, data): i53 +local function entity_index_new_id(entity_index: EntityIndex): i53 local dense_array = entity_index.dense_array if entity_index.alive_count ~= #dense_array then entity_index.alive_count += 1 local id = dense_array[entity_index.alive_count] return id end - entity_index.max_id +=1 + + entity_index.max_id += 1 local id = entity_index.max_id entity_index.alive_count += 1 dense_array[entity_index.alive_count] = id entity_index.sparse_array[id] = { - dense = entity_index.alive_count, - archetype = data + dense = entity_index.alive_count } :: Record - entity_index.sparse_count += 1 - return id end @@ -184,9 +177,15 @@ local e2v0 = entity_index_new_id(eidx, "e2v0") local e3v0 = entity_index_new_id(eidx, "e3v0") local e4v0 = entity_index_new_id(eidx, "e4v0") local e5v0 = entity_index_new_id(eidx, "e5v0") -local t = require("@testkit") -local tprint = t.print -entity_index_remove(eidx, e5v0) -local e5v1 = entity_index_new_id(eidx, "e5v1") -entity_index_remove(eidx, e2v0) -tprint(eidx) + +local e6v0 = entity_index_new_id(eidx) +entity_index_remove(eidx, e6v0) +local e6v1 = entity_index_new_id(eidx) +entity_index_remove(eidx, e6v1) +local e6v2 = entity_index_new_id(eidx) +print(ECS_ENTITY_T_LO(e6v2), ECS_GENERATION(e6v2)) + +print("-----") +local e2 = ECS_GENERATION_INC(ECS_GENERATION_INC(269)) +print("-----") +print(ECS_ENTITY_T_LO(e2), ECS_GENERATION(e2)) From 8a03dd2c39f49f1d4cc892b78198899591585384 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 03:43:56 +0100 Subject: [PATCH 05/20] Add trailing comma --- src/init.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.luau b/src/init.luau index a97d369..eb92bf1 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1886,5 +1886,5 @@ return { entity_index_try_get_any = entity_index_try_get_any, entity_index_is_alive = entity_index_is_alive, entity_index_remove = entity_index_remove, - entity_index_new_id = entity_index_new_id + entity_index_new_id = entity_index_new_id, } From a850d0b7048b208be28e4abce7c6b607982ca834 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 16:45:47 +0100 Subject: [PATCH 06/20] fast access to record --- src/init.luau | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/init.luau b/src/init.luau index eb92bf1..2b96b0a 100644 --- a/src/init.luau +++ b/src/init.luau @@ -285,9 +285,10 @@ local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: src_entities[moved] = nil :: any dst_entities[dst_row] = e1 - local record1 = entity_index_try_get_any(entity_index, e1) - local record2 = entity_index_try_get_any(entity_index, e2) + local sparse_array = entity_index.sparse_array + local record1 = sparse_array[ECS_ENTITY_T_LO(e1)] + local record2 = sparse_array[ECS_ENTITY_T_LO(e2)] record1.row = dst_row record2.row = src_row end From a3ff4e3fa0bd2a06886ed388e536787fdf1a976e Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 16:46:12 +0100 Subject: [PATCH 07/20] Bump --- wally.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wally.toml b/wally.toml index e20bfd8..c9f012d 100644 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] name = "ukendio/jecs" -version = "0.3.2" +version = "0.4.0-rc.0" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" license = "MIT" From c7ca63337b24a10eea4c79f8dab7c30ace1907cc Mon Sep 17 00:00:00 2001 From: Ukendio Date: Thu, 14 Nov 2024 21:11:23 +0100 Subject: [PATCH 08/20] 0.4.0-rc.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d7e068..3eda984 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rbxts/jecs", - "version": "0.3.3", + "version": "0.4.0-rc.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rbxts/jecs", - "version": "0.3.3", + "version": "0.4.0-rc.0", "license": "MIT", "devDependencies": { "@rbxts/compiler-types": "^2.3.0-types.1", diff --git a/package.json b/package.json index d64c35b..5c2cccc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rbxts/jecs", - "version": "0.3.3", + "version": "0.4.0-rc.0", "description": "Stupidly fast Entity Component System", "main": "src", "repository": { From 03389e518905460b28bd628ea93e779a6fd75d98 Mon Sep 17 00:00:00 2001 From: metrowaii <94939016+metrowaii@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:29:13 -0800 Subject: [PATCH 09/20] Removed redundant guard (#157) --- src/init.luau | 3 --- test/gen.luau | 24 ++++++++---------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/init.luau b/src/init.luau index 2b96b0a..3eefe61 100644 --- a/src/init.luau +++ b/src/init.luau @@ -173,9 +173,6 @@ end local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] - if not r then - return nil - end if not r or r.dense == 0 then return nil diff --git a/test/gen.luau b/test/gen.luau index 3b5599c..bf13dbf 100644 --- a/test/gen.luau +++ b/test/gen.luau @@ -49,13 +49,12 @@ type EntityIndex = { sparse_array: Map, sparse_count: number, alive_count: number, - max_id: number + max_id: number, } - -local ECS_PAIR_FLAG = 0x8 -local ECS_ID_FLAGS_MASK = 0x10 -local ECS_ENTITY_MASK = bit32.lshift(1, 24) +local ECS_PAIR_FLAG = 0x8 +local ECS_ID_FLAGS_MASK = 0x10 +local ECS_ENTITY_MASK = bit32.lshift(1, 24) local ECS_GENERATION_MASK = bit32.lshift(1, 16) -- HIGH 24 bits LOW 24 bits @@ -81,12 +80,8 @@ local function ECS_ENTITY_T_LO(e: i53): i24 return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e end - local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] - if not r then - return nil - end if not r or r.dense == 0 then return nil @@ -109,9 +104,7 @@ local function entity_index_try_get(entity_index: EntityIndex, entity: number): return r end -local function entity_index_get_alive(entity_index: EntityIndex, - entity: number): number - +local function entity_index_get_alive(entity_index: EntityIndex, entity: number): number local r = entity_index_try_get_any(entity_index, entity) if r then return entity_index.dense_array[r.dense] @@ -130,8 +123,7 @@ local function entity_index_remove(entity_index: EntityIndex, entity: number) entity_index.alive_count -= 1 local last_alive_entity = dense_array[last_entity_alive_at_index] - local r_swap = entity_index_try_get_any( - entity_index, last_alive_entity) :: Record + local r_swap = entity_index_try_get_any(entity_index, last_alive_entity) :: Record r_swap.dense = index_of_deleted_entity r.archetype = nil :: any r.row = nil :: any @@ -155,7 +147,7 @@ local function entity_index_new_id(entity_index: EntityIndex): i53 dense_array[entity_index.alive_count] = id entity_index.sparse_array[id] = { - dense = entity_index.alive_count + dense = entity_index.alive_count, } :: Record return id @@ -170,7 +162,7 @@ local eidx = { max_id = 0, sparse_array = {} :: { Record }, sparse_count = 0, - dense_array = {} :: { i53 } + dense_array = {} :: { i53 }, } local e1v0 = entity_index_new_id(eidx, "e1v0") local e2v0 = entity_index_new_id(eidx, "e2v0") From 902809b5145f1b1171b3846da15a76d02fcd9b3a Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 17:11:24 +0100 Subject: [PATCH 10/20] Fix check in debug mode --- src/init.luau | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/init.luau b/src/init.luau index 2b96b0a..06c653c 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1598,8 +1598,11 @@ if _G.__JECS_DEBUG then end end - local function ID_IS_TAG(world, id) - return not world_has_one_inline(world, ECS_ENTITY_T_HI(id), EcsComponent) + local function ID_IS_TAG(world: World, id) + if ECS_IS_PAIR(id) then + id = ecs_pair_first(world, id) + end + return not world_has_one_inline(world, id, EcsComponent) end World.query = function(world: World, ...) @@ -1886,6 +1889,5 @@ return { entity_index_try_get = entity_index_try_get, entity_index_try_get_any = entity_index_try_get_any, entity_index_is_alive = entity_index_is_alive, - entity_index_remove = entity_index_remove, entity_index_new_id = entity_index_new_id, } From 0ab66f416d3820d329f6f53fa043b9b2bdc9a3be Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 17:12:12 +0100 Subject: [PATCH 11/20] Correct indentation on numbers --- src/init.luau | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/init.luau b/src/init.luau index 61fe8b7..aaf05e3 100644 --- a/src/init.luau +++ b/src/init.luau @@ -91,8 +91,8 @@ local EcsRemove = HI_COMPONENT_ID + 10 local EcsName = HI_COMPONENT_ID + 11 local EcsRest = HI_COMPONENT_ID + 12 -local ECS_PAIR_FLAG = 0x8 -local ECS_ID_FLAGS_MASK = 0x10 +local ECS_PAIR_FLAG = 0x8 +local ECS_ID_FLAGS_MASK = 0x10 local ECS_ENTITY_MASK = bit32.lshift(1, 24) local ECS_GENERATION_MASK = bit32.lshift(1, 16) @@ -173,6 +173,9 @@ end local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] + if not r then + return nil + end if not r or r.dense == 0 then return nil @@ -1610,14 +1613,12 @@ if _G.__JECS_DEBUG then World.set = function(world: World, entity: i53, id: i53, value: any): () local is_tag = ID_IS_TAG(world, id) if is_tag and value == nil then - world_add(world, entity, id) local _1 = get_name(world, entity) local _2 = get_name(world, id) local why = "cannot set component value to nil" throw(why) return elseif value ~= nil and is_tag then - world_add(world, entity, id) local _1 = get_name(world, entity) local _2 = get_name(world, id) local why = `cannot set a component value because {_2} is a tag` @@ -1631,10 +1632,8 @@ if _G.__JECS_DEBUG then World.add = function(world: World, entity: i53, id: i53, value: nil) if value ~= nil then - local _1 = get_name(world, entity) - local _2 = get_name(world, id) + local _1 = get_name(world, entity) local _2 = get_name(world, id) throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`) - return end world_add(world, entity, id) From eb315067f552c672ffb6735dfc5cefbdde6bc4fb Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 17:25:57 +0100 Subject: [PATCH 12/20] Fast paths on entity_index_try_get for user facing api --- src/init.luau | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/init.luau b/src/init.luau index aaf05e3..43d08a0 100644 --- a/src/init.luau +++ b/src/init.luau @@ -198,6 +198,16 @@ local function entity_index_try_get(entity_index: EntityIndex, entity: number): return r end +local function entity_index_try_get_fast(entity_index: EntityIndex, entity: number): Record? + local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)] + if r then + if entity_index.dense_array[r.dense] ~= entity then + return nil + end + end + return r +end + local function entity_index_get_alive(index: EntityIndex, e: i24): i53 local r = entity_index_try_get_any(index, e) if r then @@ -387,7 +397,7 @@ local function world_get_one_inline(world: World, entity: i53, id: i53): any end local function world_has_one_inline(world: World, entity: number, id: i53): boolean - local record = entity_index_try_get(world.entity_index, entity) + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return false end @@ -403,7 +413,7 @@ local function world_has_one_inline(world: World, entity: number, id: i53): bool end local function world_has(world: World, entity: number, ...: i53): boolean - local record = entity_index_try_get(world.entity_index, entity) + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return false end @@ -426,7 +436,7 @@ end local function world_target(world: World, entity: i53, relation: i24, index: number?): i24? local nth = index or 0 - local record = entity_index_try_get(world.entity_index, entity) + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return nil end @@ -735,7 +745,7 @@ end local function world_add(world: World, entity: i53, id: i53): () local entity_index = world.entity_index - local record = entity_index_try_get(entity_index, entity) + local record = entity_index_try_get_fast(entity_index, entity) if not record then return end @@ -763,7 +773,7 @@ end local function world_set(world: World, entity: i53, id: i53, data: unknown): () local entity_index = world.entity_index - local record = entity_index_try_get(entity_index, entity) + local record = entity_index_try_get_fast(entity_index, entity) if not record then return end @@ -836,7 +846,7 @@ end local function world_remove(world: World, entity: i53, id: i53) local entity_index = world.entity_index - local record = entity_index_try_get(entity_index, entity) + local record = entity_index_try_get_fast(entity_index, entity) if not record then return end From bacda3c2e8ddcb78ca906ba34413ba5338fed2d9 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 18:05:17 +0100 Subject: [PATCH 13/20] Use ecs_pair_first --- src/init.luau | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/init.luau b/src/init.luau index 43d08a0..00548a2 100644 --- a/src/init.luau +++ b/src/init.luau @@ -348,7 +348,7 @@ do end function world_get(world: World, entity: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any - local record = entity_index_try_get(world.entity_index, entity) + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return nil end @@ -379,7 +379,7 @@ do end local function world_get_one_inline(world: World, entity: i53, id: i53): any - local record = entity_index_try_get(world.entity_index, entity) + local record = entity_index_try_get_fast(world.entity_index, entity) if not record then return nil end @@ -482,7 +482,10 @@ local function id_record_ensure(world: World, id: number): IdRecord if not idr then local flags = ECS_ID_MASK - local relation = ECS_ENTITY_T_HI(id) + local relation = id + if ECS_IS_PAIR(id) then + relation = ecs_pair_first(world, id) + end local cleanup_policy = world_target(world, relation, EcsOnDelete, 0) local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0) From a5747a6145c7a686edf8eac022ac462ec9dc5fe7 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 18:05:45 +0100 Subject: [PATCH 14/20] Add newline --- src/init.luau | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.luau b/src/init.luau index 00548a2..d9b4434 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1645,7 +1645,8 @@ if _G.__JECS_DEBUG then World.add = function(world: World, entity: i53, id: i53, value: nil) if value ~= nil then - local _1 = get_name(world, entity) local _2 = get_name(world, id) + local _1 = get_name(world, entity) + local _2 = get_name(world, id) throw("You provided a value when none was expected. " .. `Did you mean to use "world:add({_1}, {_2})"`) end From 5de3f7dd15a0a1debb3da2679202c7b3797b92f7 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 18:07:12 +0100 Subject: [PATCH 15/20] Remove typeof --- test/tests.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.luau b/test/tests.luau index efa86cd..a74b5f3 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1,4 +1,4 @@ -local jecs: typeof(require("../jecs/src")) = require("@jecs") +local jecs = require("@jecs") local testkit = require("@testkit") local BENCH, START = testkit.benchmark() From ea7c118db6d9ca1b0c410e39cc19b7ed692e8f91 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sat, 16 Nov 2024 19:16:51 +0100 Subject: [PATCH 16/20] Entity defaults to Tag --- src/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.d.ts b/src/index.d.ts index 53d436c..f6e5e7e 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -2,7 +2,7 @@ * A unique identifier in the world, entity. * The generic type T defines the data type when this entity is used as a component */ -export type Entity = number & { __jecs_value: T }; +export type Entity = number & { __jecs_value: T }; /** * An entity with no associated data when used as a component From 0108c6ffc67b68f85ffff3877e0b123f3aa4af0d Mon Sep 17 00:00:00 2001 From: EncodedVenom <32179912+EncodedVenom@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:14:12 -0500 Subject: [PATCH 17/20] Pin luau version to 0.651 --- .github/workflows/unit-testing.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit-testing.yaml b/.github/workflows/unit-testing.yaml index 5e18765..19b38f5 100644 --- a/.github/workflows/unit-testing.yaml +++ b/.github/workflows/unit-testing.yaml @@ -13,7 +13,9 @@ jobs: uses: actions/checkout@v4 - name: Install Luau - uses: encodedvenom/install-luau@v2.1 + uses: encodedvenom/install-luau@v4 + with: + version: '0.651' - name: Run Unit Tests id: run_tests From 395465d7c905263d73865b7e12a0b2c71c7565b1 Mon Sep 17 00:00:00 2001 From: EncodedVenom <32179912+EncodedVenom@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:18:22 -0500 Subject: [PATCH 18/20] Update unit-testing.yaml --- .github/workflows/unit-testing.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-testing.yaml b/.github/workflows/unit-testing.yaml index 19b38f5..eda6476 100644 --- a/.github/workflows/unit-testing.yaml +++ b/.github/workflows/unit-testing.yaml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v4 - name: Install Luau - uses: encodedvenom/install-luau@v4 + uses: encodedvenom/install-luau@v4.1 with: version: '0.651' From 12236022c229c42e7663ef69f3440e9b227e6451 Mon Sep 17 00:00:00 2001 From: EncodedVenom <32179912+EncodedVenom@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:20:16 -0500 Subject: [PATCH 19/20] Update unit-testing.yaml --- .github/workflows/unit-testing.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/unit-testing.yaml b/.github/workflows/unit-testing.yaml index eda6476..5fe621b 100644 --- a/.github/workflows/unit-testing.yaml +++ b/.github/workflows/unit-testing.yaml @@ -16,6 +16,7 @@ jobs: uses: encodedvenom/install-luau@v4.1 with: version: '0.651' + verbose: 'true' - name: Run Unit Tests id: run_tests From 638272bfcfbeb6265af9ca2649373cbd65f7170f Mon Sep 17 00:00:00 2001 From: EncodedVenom <32179912+EncodedVenom@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:38:42 -0500 Subject: [PATCH 20/20] Update unit-testing.yaml --- .github/workflows/unit-testing.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-testing.yaml b/.github/workflows/unit-testing.yaml index 5fe621b..01a9981 100644 --- a/.github/workflows/unit-testing.yaml +++ b/.github/workflows/unit-testing.yaml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v4 - name: Install Luau - uses: encodedvenom/install-luau@v4.1 + uses: encodedvenom/install-luau@v4.2 with: version: '0.651' verbose: 'true'