mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
buh
This commit is contained in:
parent
cda04ce5a9
commit
b3f8e2504e
8 changed files with 19491 additions and 178 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -50,3 +50,5 @@ WallyPatches
|
|||
roblox.toml
|
||||
sourcemap.json
|
||||
drafts/*.lua
|
||||
|
||||
*.code-workspace
|
||||
|
|
73
lib/Types.luau
Normal file
73
lib/Types.luau
Normal file
|
@ -0,0 +1,73 @@
|
|||
--!native
|
||||
--!optimize 2
|
||||
--!strict
|
||||
|
||||
export type i53 = number
|
||||
export type i24 = number
|
||||
|
||||
export type Ty = {i53}
|
||||
export type ArchetypeId = number
|
||||
|
||||
export type Column = {any}
|
||||
|
||||
export type Archetype = {
|
||||
id: number,
|
||||
edges: {
|
||||
[i24]: {
|
||||
add: Archetype,
|
||||
remove: Archetype,
|
||||
},
|
||||
},
|
||||
types: Ty,
|
||||
type: string | number,
|
||||
entities: {number},
|
||||
columns: {Column},
|
||||
records: {},
|
||||
}
|
||||
|
||||
export type Record = {
|
||||
archetype: Archetype,
|
||||
row: number,
|
||||
}
|
||||
|
||||
export type EntityIndex = {[i24]: Record}
|
||||
export type ComponentIndex = {[i24]: ArchetypeMap}
|
||||
|
||||
export type ArchetypeRecord = number
|
||||
export type ArchetypeMap = {sparse: {[ArchetypeId]: ArchetypeRecord}, size: number}
|
||||
export type Archetypes = {[ArchetypeId]: Archetype}
|
||||
|
||||
export type ArchetypeDiff = {
|
||||
added: Ty,
|
||||
removed: Ty,
|
||||
}
|
||||
|
||||
export type Hook = {
|
||||
ids: Ty,
|
||||
archetype: Archetype,
|
||||
otherArchetype: Archetype,
|
||||
offset: number,
|
||||
}
|
||||
|
||||
export type EventDescription = Hook & {
|
||||
event: number,
|
||||
}
|
||||
|
||||
export type World = {
|
||||
archetypeIndex: {[number | string]: Archetype},
|
||||
archetypes: {[number]: Archetype},
|
||||
componentIndex: {[i24]: ArchetypeMap},
|
||||
entityIndex: {[i53]: Record},
|
||||
hooks: {[number]: {Hook}},
|
||||
nextComponentId: number,
|
||||
nextEntityId: number,
|
||||
ROOT_ARCHETYPE: Archetype,
|
||||
|
||||
get: (self: World) -> (),
|
||||
}
|
||||
|
||||
export type WorldStatic = {
|
||||
new: () -> World,
|
||||
}
|
||||
|
||||
return false
|
324
lib/init.lua
324
lib/init.lua
|
@ -3,45 +3,29 @@
|
|||
--!strict
|
||||
--draft 4
|
||||
|
||||
type i53 = number
|
||||
type i24 = number
|
||||
local Types = require(script.Types)
|
||||
|
||||
type Ty = { i53 }
|
||||
type ArchetypeId = number
|
||||
type i53 = Types.i53
|
||||
type i24 = Types.i24
|
||||
|
||||
type Column = { any }
|
||||
type Ty = Types.Ty
|
||||
type ArchetypeId = Types.ArchetypeId
|
||||
|
||||
type Archetype = {
|
||||
id: number,
|
||||
edges: {
|
||||
[i24]: {
|
||||
add: Archetype,
|
||||
remove: Archetype,
|
||||
},
|
||||
},
|
||||
types: Ty,
|
||||
type: string | number,
|
||||
entities: { number },
|
||||
columns: { Column },
|
||||
records: {},
|
||||
}
|
||||
type Column = Types.Column
|
||||
|
||||
type Record = {
|
||||
archetype: Archetype,
|
||||
row: number,
|
||||
}
|
||||
type Archetype = Types.Archetype
|
||||
type Record = Types.Record
|
||||
|
||||
type EntityIndex = { [i24]: Record }
|
||||
type ComponentIndex = { [i24]: ArchetypeMap}
|
||||
type EntityIndex = Types.EntityIndex
|
||||
type ComponentIndex = Types.ComponentIndex
|
||||
|
||||
type ArchetypeRecord = number
|
||||
type ArchetypeMap = { sparse: { [ArchetypeId]: ArchetypeRecord } , size: number }
|
||||
type Archetypes = { [ArchetypeId]: Archetype }
|
||||
|
||||
type ArchetypeDiff = {
|
||||
added: Ty,
|
||||
removed: Ty,
|
||||
}
|
||||
type ArchetypeRecord = Types.ArchetypeRecord
|
||||
type ArchetypeMap = Types.ArchetypeMap
|
||||
type Archetypes = Types.Archetypes
|
||||
|
||||
type ArchetypeDiff = Types.ArchetypeDiff
|
||||
|
||||
-- type World = Types.World
|
||||
|
||||
local HI_COMPONENT_ID = 256
|
||||
local ON_ADD = HI_COMPONENT_ID + 1
|
||||
|
@ -64,17 +48,17 @@ local function transitionArchetype(
|
|||
local types = from.types
|
||||
|
||||
for i, column in columns do
|
||||
-- Retrieves the new column index from the source archetype's record from each component
|
||||
-- Retrieves the new column index from the source archetype's record from each component
|
||||
-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
|
||||
local targetColumn = destinationColumns[tr[types[i]]]
|
||||
|
||||
-- Sometimes target column may not exist, e.g. when you remove a component.
|
||||
if targetColumn then
|
||||
if targetColumn then
|
||||
targetColumn[destinationRow] = column[sourceRow]
|
||||
end
|
||||
-- If the entity is the last row in the archetype then swapping it would be meaningless.
|
||||
local last = #column
|
||||
if sourceRow ~= last then
|
||||
if sourceRow ~= last then
|
||||
-- Swap rempves columns to ensure there are no holes in the archetype.
|
||||
column[sourceRow] = column[last]
|
||||
end
|
||||
|
@ -88,11 +72,11 @@ local function transitionArchetype(
|
|||
-- Because we have swapped columns we now have to update the records
|
||||
-- corresponding to the entities' rows that were swapped.
|
||||
local movedAway = #sourceEntities
|
||||
if sourceRow ~= movedAway then
|
||||
if sourceRow ~= movedAway then
|
||||
sourceEntities[sourceRow] = sourceEntities[movedAway]
|
||||
entityIndex[sourceEntities[movedAway]].row = sourceRow
|
||||
end
|
||||
|
||||
|
||||
sourceEntities[movedAway] = nil
|
||||
end
|
||||
|
||||
|
@ -130,7 +114,7 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
|||
local destinationId = destinationIds[i]
|
||||
|
||||
if not componentIndex[destinationId] then
|
||||
componentIndex[destinationId] = { size = 0, sparse = {} }
|
||||
componentIndex[destinationId] = {size = 0, sparse = {}}
|
||||
end
|
||||
|
||||
local archetypesMap = componentIndex[destinationId]
|
||||
|
@ -139,30 +123,30 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
|||
end
|
||||
end
|
||||
|
||||
local function archetypeOf(world: World, types: { i24 }, prev: Archetype?): Archetype
|
||||
local function archetypeOf(world: World, types: {i24}, prev: Archetype?): Archetype
|
||||
local ty = hash(types)
|
||||
|
||||
world.nextArchetypeId = (world.nextArchetypeId::number)+ 1
|
||||
local id = world.nextArchetypeId
|
||||
world.nextArchetypeId = (world.nextArchetypeId :: number) + 1
|
||||
local id = world.nextArchetypeId
|
||||
|
||||
local columns = {} :: { any }
|
||||
local columns = {} :: {any}
|
||||
|
||||
for _ in types do
|
||||
table.insert(columns, {})
|
||||
end
|
||||
|
||||
local archetype = {
|
||||
id = id,
|
||||
types = types,
|
||||
type = ty,
|
||||
columns = columns,
|
||||
entities = {},
|
||||
edges = {},
|
||||
records = {},
|
||||
id = id;
|
||||
types = types;
|
||||
type = ty;
|
||||
columns = columns;
|
||||
entities = {};
|
||||
edges = {};
|
||||
records = {};
|
||||
}
|
||||
world.archetypeIndex[ty] = archetype
|
||||
world.archetypes[id] = archetype
|
||||
if #types > 0 then
|
||||
if #types > 0 then
|
||||
createArchetypeRecords(world.componentIndex, archetype, prev)
|
||||
end
|
||||
|
||||
|
@ -171,42 +155,42 @@ end
|
|||
|
||||
local World = {}
|
||||
World.__index = World
|
||||
function World.new()
|
||||
function World.new()
|
||||
local self = setmetatable({
|
||||
entityIndex = {},
|
||||
componentIndex = {},
|
||||
archetypes = {},
|
||||
archetypeIndex = {},
|
||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||
nextEntityId = 0,
|
||||
nextComponentId = 0,
|
||||
nextArchetypeId = 0,
|
||||
entityIndex = {};
|
||||
componentIndex = {};
|
||||
archetypes = {};
|
||||
archetypeIndex = {};
|
||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype;
|
||||
nextEntityId = 0;
|
||||
nextComponentId = 0;
|
||||
nextArchetypeId = 0;
|
||||
hooks = {
|
||||
[ON_ADD] = {}
|
||||
}
|
||||
[ON_ADD] = {};
|
||||
};
|
||||
}, World)
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
local function emit(world, eventDescription)
|
||||
local function emit(world, eventDescription)
|
||||
local event = eventDescription.event
|
||||
|
||||
table.insert(world.hooks[event], {
|
||||
ids = eventDescription.ids,
|
||||
archetype = eventDescription.archetype,
|
||||
otherArchetype = eventDescription.otherArchetype,
|
||||
offset = eventDescription.offset
|
||||
ids = eventDescription.ids;
|
||||
archetype = eventDescription.archetype;
|
||||
otherArchetype = eventDescription.otherArchetype;
|
||||
offset = eventDescription.offset;
|
||||
})
|
||||
end
|
||||
|
||||
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
||||
if #added > 0 then
|
||||
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
||||
if #added > 0 then
|
||||
emit(world, {
|
||||
event = ON_ADD,
|
||||
ids = added,
|
||||
archetype = archetype,
|
||||
otherArchetype = otherArchetype,
|
||||
offset = row,
|
||||
event = ON_ADD;
|
||||
ids = added;
|
||||
archetype = archetype;
|
||||
otherArchetype = otherArchetype;
|
||||
offset = row;
|
||||
})
|
||||
end
|
||||
end
|
||||
|
@ -217,7 +201,7 @@ local function ensureArchetype(world: World, types, prev)
|
|||
if #types < 1 then
|
||||
return world.ROOT_ARCHETYPE
|
||||
end
|
||||
|
||||
|
||||
local ty = hash(types)
|
||||
local archetype = world.archetypeIndex[ty]
|
||||
if archetype then
|
||||
|
@ -227,7 +211,7 @@ local function ensureArchetype(world: World, types, prev)
|
|||
return archetypeOf(world, types, prev)
|
||||
end
|
||||
|
||||
local function findInsert(types: { i53 }, toAdd: i53)
|
||||
local function findInsert(types: {i53}, toAdd: i53)
|
||||
local count = #types
|
||||
for i = 1, count do
|
||||
local id = types[i]
|
||||
|
@ -244,7 +228,7 @@ end
|
|||
local function findArchetypeWith(world: World, node: Archetype, componentId: i53)
|
||||
local types = node.types
|
||||
-- Component IDs are added incrementally, so inserting and sorting
|
||||
-- them each time would be expensive. Instead this insertion sort can find the insertion
|
||||
-- them each time would be expensive. Instead this insertion sort can find the insertion
|
||||
-- point in the types array.
|
||||
local at = findInsert(types, componentId)
|
||||
if at == -1 then
|
||||
|
@ -266,18 +250,18 @@ local function ensureEdge(archetype: Archetype, componentId: i53)
|
|||
end
|
||||
|
||||
local function archetypeTraverseAdd(world: World, componentId: i53, from: Archetype): Archetype
|
||||
if not from then
|
||||
if not from then
|
||||
-- If there was no source archetype then it should return the ROOT_ARCHETYPE
|
||||
if not world.ROOT_ARCHETYPE then
|
||||
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
||||
end
|
||||
if not world.ROOT_ARCHETYPE then
|
||||
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
||||
end
|
||||
from = world.ROOT_ARCHETYPE
|
||||
end
|
||||
local edge = ensureEdge(from, componentId)
|
||||
|
||||
if not edge.add then
|
||||
-- Save an edge using the component ID to the archetype to allow
|
||||
-- Save an edge using the component ID to the archetype to allow
|
||||
-- faster traversals to adjacent archetypes.
|
||||
edge.add = findArchetypeWith(world, from, componentId)
|
||||
end
|
||||
|
@ -293,14 +277,14 @@ local function ensureRecord(entityIndex, entityId: i53): Record
|
|||
return entityIndex[id] :: Record
|
||||
end
|
||||
|
||||
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||
function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||
local record = ensureRecord(world.entityIndex, entityId)
|
||||
local from = record.archetype
|
||||
local to = archetypeTraverseAdd(world, componentId, from)
|
||||
|
||||
if from == to then
|
||||
-- If the archetypes are the same it can avoid moving the entity
|
||||
-- and just set the data directly.
|
||||
if from == to then
|
||||
-- If the archetypes are the same it can avoid moving the entity
|
||||
-- and just set the data directly.
|
||||
local archetypeRecord = to.records[componentId]
|
||||
from.columns[archetypeRecord][record.row] = data
|
||||
-- Should fire an OnSet event here.
|
||||
|
@ -308,13 +292,13 @@ function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
|||
end
|
||||
|
||||
if from then
|
||||
-- If there was a previous archetype, then the entity needs to move the archetype
|
||||
-- If there was a previous archetype, then the entity needs to move the archetype
|
||||
moveEntity(world.entityIndex, entityId, record, to)
|
||||
else
|
||||
if #to.types > 0 then
|
||||
-- When there is no previous archetype it should create the archetype
|
||||
newEntity(entityId, record, to)
|
||||
onNotifyAdd(world, to, from, record.row, { componentId })
|
||||
onNotifyAdd(world, to, from, record.row, {componentId})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -326,9 +310,8 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
|
|||
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
||||
local edge = ensureEdge(from, componentId)
|
||||
|
||||
|
||||
if not edge.remove then
|
||||
local to = table.clone(from.types)
|
||||
local to = table.clone(from.types)
|
||||
table.remove(to, table.find(to, componentId))
|
||||
edge.remove = ensureArchetype(world, to, from)
|
||||
end
|
||||
|
@ -336,18 +319,18 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
|
|||
return edge.remove
|
||||
end
|
||||
|
||||
function World.remove(world: World, entityId: i53, componentId: i53)
|
||||
function World.remove(world: World, entityId: i53, componentId: i53)
|
||||
local record = ensureRecord(world.entityIndex, entityId)
|
||||
local sourceArchetype = record.archetype
|
||||
local destinationArchetype = archetypeTraverseRemove(world, componentId, sourceArchetype)
|
||||
|
||||
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
|
||||
if sourceArchetype and not (sourceArchetype == destinationArchetype) then
|
||||
moveEntity(world.entityIndex, entityId, record, destinationArchetype)
|
||||
end
|
||||
end
|
||||
|
||||
-- Keeping the function as small as possible to enable inlining
|
||||
local function get(componentIndex: { [i24]: ArchetypeMap }, record: Record, componentId: i24)
|
||||
local function get(componentIndex: {[i24]: ArchetypeMap}, record: Record, componentId: i24): number?
|
||||
local archetype = record.archetype
|
||||
local archetypeRecord = archetype.records[componentId]
|
||||
|
||||
|
@ -360,7 +343,7 @@ end
|
|||
|
||||
function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?)
|
||||
local id = entityId
|
||||
local componentIndex = world.componentIndex
|
||||
local componentIndex = world.componentIndex
|
||||
local record = world.entityIndex[id]
|
||||
if not record then
|
||||
return nil
|
||||
|
@ -382,13 +365,12 @@ function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53
|
|||
end
|
||||
|
||||
local function noop(self: Query, ...: i53): () -> (number, ...any)
|
||||
return function()
|
||||
end :: any
|
||||
return function() end :: any
|
||||
end
|
||||
|
||||
local EmptyQuery = {
|
||||
__iter = noop,
|
||||
without = noop
|
||||
__iter = noop;
|
||||
without = noop;
|
||||
}
|
||||
EmptyQuery.__index = EmptyQuery
|
||||
setmetatable(EmptyQuery, EmptyQuery)
|
||||
|
@ -397,24 +379,24 @@ export type Query = typeof(EmptyQuery)
|
|||
|
||||
function World.query(world: World, ...: i53): Query
|
||||
local compatibleArchetypes = {}
|
||||
local components = { ... }
|
||||
local components = {...}
|
||||
local archetypes = world.archetypes
|
||||
local queryLength = #components
|
||||
|
||||
if queryLength == 0 then
|
||||
if queryLength == 0 then
|
||||
error("Missing components")
|
||||
end
|
||||
|
||||
local firstArchetypeMap
|
||||
local componentIndex = world.componentIndex
|
||||
|
||||
for i, componentId in components do
|
||||
for i, componentId in components do
|
||||
local map = componentIndex[componentId]
|
||||
if not map then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
|
||||
if firstArchetypeMap == nil or map.size < firstArchetypeMap.size then
|
||||
firstArchetypeMap = map
|
||||
end
|
||||
end
|
||||
|
@ -422,110 +404,102 @@ function World.query(world: World, ...: i53): Query
|
|||
for id in firstArchetypeMap.sparse do
|
||||
local archetype = archetypes[id]
|
||||
local archetypeRecords = archetype.records
|
||||
local indices = {}
|
||||
local indices = {}
|
||||
local skip = false
|
||||
|
||||
for i, componentId in components do
|
||||
|
||||
for i, componentId in components do
|
||||
local index = archetypeRecords[componentId]
|
||||
if not index then
|
||||
if not index then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
indices[i] = archetypeRecords[componentId]
|
||||
end
|
||||
|
||||
if skip then
|
||||
if skip then
|
||||
continue
|
||||
end
|
||||
table.insert(compatibleArchetypes, { archetype, indices })
|
||||
table.insert(compatibleArchetypes, {archetype, indices})
|
||||
end
|
||||
|
||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||
if not lastArchetype then
|
||||
if not lastArchetype then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
|
||||
local preparedQuery = {}
|
||||
preparedQuery.__index = preparedQuery
|
||||
|
||||
function preparedQuery:without(...)
|
||||
local components = { ... }
|
||||
for i = #compatibleArchetypes, 1, -1 do
|
||||
function preparedQuery:without(...)
|
||||
local components = {...}
|
||||
for i = #compatibleArchetypes, 1, -1 do
|
||||
local archetype = compatibleArchetypes[i][1]
|
||||
local shouldRemove = false
|
||||
for _, componentId in components do
|
||||
if archetype.records[componentId] then
|
||||
for _, componentId in components do
|
||||
if archetype.records[componentId] then
|
||||
shouldRemove = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if shouldRemove then
|
||||
if shouldRemove then
|
||||
table.remove(compatibleArchetypes, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||
if not lastArchetype then
|
||||
if not lastArchetype then
|
||||
return EmptyQuery
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
local lastRow
|
||||
local queryOutput = {}
|
||||
|
||||
|
||||
function preparedQuery:__iter()
|
||||
return function()
|
||||
function preparedQuery:__iter()
|
||||
return function()
|
||||
local archetype = compatibleArchetype[1]
|
||||
local row = next(archetype.entities, lastRow)
|
||||
while row == nil do
|
||||
while row == nil do
|
||||
lastArchetype, compatibleArchetype = next(compatibleArchetypes, lastArchetype)
|
||||
if lastArchetype == nil then
|
||||
return
|
||||
if lastArchetype == nil then
|
||||
return
|
||||
end
|
||||
archetype = compatibleArchetype[1]
|
||||
row = next(archetype.entities, row)
|
||||
end
|
||||
lastRow = row
|
||||
|
||||
|
||||
local entityId = archetype.entities[row :: number]
|
||||
local columns = archetype.columns
|
||||
local tr = compatibleArchetype[2]
|
||||
|
||||
if queryLength == 1 then
|
||||
|
||||
if queryLength == 1 then
|
||||
return entityId, columns[tr[1]][row]
|
||||
elseif queryLength == 2 then
|
||||
elseif queryLength == 2 then
|
||||
return entityId, columns[tr[1]][row], columns[tr[2]][row]
|
||||
elseif queryLength == 3 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row]
|
||||
elseif queryLength == 4 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row],
|
||||
columns[tr[4]][row]
|
||||
elseif queryLength == 5 then
|
||||
return entityId,
|
||||
elseif queryLength == 3 then
|
||||
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row]
|
||||
elseif queryLength == 4 then
|
||||
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row]
|
||||
elseif queryLength == 5 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row],
|
||||
columns[tr[4]][row],
|
||||
columns[tr[5]][row]
|
||||
elseif queryLength == 6 then
|
||||
return entityId,
|
||||
elseif queryLength == 6 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row],
|
||||
columns[tr[4]][row],
|
||||
columns[tr[5]][row],
|
||||
columns[tr[6]][row]
|
||||
elseif queryLength == 7 then
|
||||
return entityId,
|
||||
elseif queryLength == 7 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row],
|
||||
|
@ -533,8 +507,8 @@ function World.query(world: World, ...: i53): Query
|
|||
columns[tr[5]][row],
|
||||
columns[tr[6]][row],
|
||||
columns[tr[7]][row]
|
||||
elseif queryLength == 8 then
|
||||
return entityId,
|
||||
elseif queryLength == 8 then
|
||||
return entityId,
|
||||
columns[tr[1]][row],
|
||||
columns[tr[2]][row],
|
||||
columns[tr[3]][row],
|
||||
|
@ -545,7 +519,7 @@ function World.query(world: World, ...: i53): Query
|
|||
columns[tr[8]][row]
|
||||
end
|
||||
|
||||
for i in components do
|
||||
for i in components do
|
||||
queryOutput[i] = tr[i][row]
|
||||
end
|
||||
|
||||
|
@ -556,12 +530,12 @@ function World.query(world: World, ...: i53): Query
|
|||
return setmetatable({}, preparedQuery) :: any
|
||||
end
|
||||
|
||||
function World.component(world: World)
|
||||
local componentId = world.nextComponentId + 1
|
||||
if componentId > HI_COMPONENT_ID then
|
||||
-- IDs are partitioned into ranges because component IDs are not nominal,
|
||||
function World.component(world: World)
|
||||
local componentId = world.nextComponentId + 1
|
||||
if componentId > HI_COMPONENT_ID then
|
||||
-- IDs are partitioned into ranges because component IDs are not nominal,
|
||||
-- so it needs to error when IDs intersect into the entity range.
|
||||
error("Too many components, consider using world:entity() instead to create components.")
|
||||
error("Too many components, consider using world:entity() instead to create components.")
|
||||
end
|
||||
world.nextComponentId = componentId
|
||||
return componentId
|
||||
|
@ -572,7 +546,7 @@ function World.entity(world: World)
|
|||
return world.nextEntityId + REST
|
||||
end
|
||||
|
||||
function World.delete(world: World, entityId: i53)
|
||||
function World.delete(world: World, entityId: i53)
|
||||
local entityIndex = world.entityIndex
|
||||
local record = entityIndex[entityId]
|
||||
moveEntity(entityIndex, entityId, record, world.ROOT_ARCHETYPE)
|
||||
|
@ -584,57 +558,57 @@ function World.delete(world: World, entityId: i53)
|
|||
end
|
||||
|
||||
function World.observer(world: World, ...)
|
||||
local componentIds = { ... }
|
||||
|
||||
local componentIds = {...}
|
||||
|
||||
return {
|
||||
event = function(event)
|
||||
event = function(event)
|
||||
local hook = world.hooks[event]
|
||||
world.hooks[event] = nil
|
||||
|
||||
local last, change
|
||||
return function()
|
||||
return function()
|
||||
last, change = next(hook, last)
|
||||
if not last then
|
||||
if not last then
|
||||
return
|
||||
end
|
||||
|
||||
local matched = false
|
||||
|
||||
while not matched do
|
||||
|
||||
while not matched do
|
||||
local skip = false
|
||||
for _, id in change.ids do
|
||||
if not table.find(componentIds, id) then
|
||||
for _, id in change.ids do
|
||||
if not table.find(componentIds, id) then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if skip then
|
||||
|
||||
if skip then
|
||||
last, change = next(hook, last)
|
||||
continue
|
||||
end
|
||||
|
||||
matched = true
|
||||
end
|
||||
|
||||
|
||||
local queryOutput = {}
|
||||
local row = change.offset
|
||||
local archetype = change.archetype
|
||||
local columns = archetype.columns
|
||||
local archetypeRecords = archetype.records
|
||||
for _, id in componentIds do
|
||||
for _, id in componentIds do
|
||||
table.insert(queryOutput, columns[archetypeRecords[id]][row])
|
||||
end
|
||||
|
||||
return archetype.entities[row], unpack(queryOutput, 1, #queryOutput)
|
||||
end
|
||||
end
|
||||
end;
|
||||
}
|
||||
end
|
||||
|
||||
return table.freeze({
|
||||
World = World,
|
||||
ON_ADD = ON_ADD,
|
||||
ON_REMOVE = ON_REMOVE,
|
||||
ON_SET = ON_SET
|
||||
World = World;
|
||||
ON_ADD = ON_ADD;
|
||||
ON_REMOVE = ON_REMOVE;
|
||||
ON_SET = ON_SET;
|
||||
})
|
||||
|
|
19255
roblox.yml
Normal file
19255
roblox.yml
Normal file
File diff suppressed because it is too large
Load diff
4
selene.toml
Normal file
4
selene.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
std = "roblox"
|
||||
|
||||
[lints]
|
||||
global_usage = "allow"
|
5
stylua.toml
Normal file
5
stylua.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
column_width = 120
|
||||
quote_style = "ForceDouble"
|
||||
|
||||
[sort_requires]
|
||||
enabled = true
|
3
testez-companion.toml
Normal file
3
testez-companion.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
roots = ["ServerStorage"]
|
||||
|
||||
[extraOptions]
|
|
@ -10,6 +10,3 @@ include = ["default.project.json", "lib", "wally.toml", "README.md"]
|
|||
TestEZ = "roblox/testez@0.4.1"
|
||||
Matter = "matter-ecs/matter@0.8.0"
|
||||
ecr = "centau/ecr@0.8.0"
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue