mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 17:40:02 +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
|
roblox.toml
|
||||||
sourcemap.json
|
sourcemap.json
|
||||||
drafts/*.lua
|
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
|
166
lib/init.lua
166
lib/init.lua
|
@ -3,45 +3,29 @@
|
||||||
--!strict
|
--!strict
|
||||||
--draft 4
|
--draft 4
|
||||||
|
|
||||||
type i53 = number
|
local Types = require(script.Types)
|
||||||
type i24 = number
|
|
||||||
|
|
||||||
type Ty = { i53 }
|
type i53 = Types.i53
|
||||||
type ArchetypeId = number
|
type i24 = Types.i24
|
||||||
|
|
||||||
type Column = { any }
|
type Ty = Types.Ty
|
||||||
|
type ArchetypeId = Types.ArchetypeId
|
||||||
|
|
||||||
type Archetype = {
|
type Column = Types.Column
|
||||||
id: number,
|
|
||||||
edges: {
|
|
||||||
[i24]: {
|
|
||||||
add: Archetype,
|
|
||||||
remove: Archetype,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
types: Ty,
|
|
||||||
type: string | number,
|
|
||||||
entities: { number },
|
|
||||||
columns: { Column },
|
|
||||||
records: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
type Record = {
|
type Archetype = Types.Archetype
|
||||||
archetype: Archetype,
|
type Record = Types.Record
|
||||||
row: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
type EntityIndex = { [i24]: Record }
|
type EntityIndex = Types.EntityIndex
|
||||||
type ComponentIndex = { [i24]: ArchetypeMap}
|
type ComponentIndex = Types.ComponentIndex
|
||||||
|
|
||||||
type ArchetypeRecord = number
|
type ArchetypeRecord = Types.ArchetypeRecord
|
||||||
type ArchetypeMap = { sparse: { [ArchetypeId]: ArchetypeRecord } , size: number }
|
type ArchetypeMap = Types.ArchetypeMap
|
||||||
type Archetypes = { [ArchetypeId]: Archetype }
|
type Archetypes = Types.Archetypes
|
||||||
|
|
||||||
type ArchetypeDiff = {
|
type ArchetypeDiff = Types.ArchetypeDiff
|
||||||
added: Ty,
|
|
||||||
removed: Ty,
|
-- type World = Types.World
|
||||||
}
|
|
||||||
|
|
||||||
local HI_COMPONENT_ID = 256
|
local HI_COMPONENT_ID = 256
|
||||||
local ON_ADD = HI_COMPONENT_ID + 1
|
local ON_ADD = HI_COMPONENT_ID + 1
|
||||||
|
@ -130,7 +114,7 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
||||||
local destinationId = destinationIds[i]
|
local destinationId = destinationIds[i]
|
||||||
|
|
||||||
if not componentIndex[destinationId] then
|
if not componentIndex[destinationId] then
|
||||||
componentIndex[destinationId] = { size = 0, sparse = {} }
|
componentIndex[destinationId] = {size = 0, sparse = {}}
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetypesMap = componentIndex[destinationId]
|
local archetypesMap = componentIndex[destinationId]
|
||||||
|
@ -139,26 +123,26 @@ local function createArchetypeRecords(componentIndex: ComponentIndex, to: Archet
|
||||||
end
|
end
|
||||||
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)
|
local ty = hash(types)
|
||||||
|
|
||||||
world.nextArchetypeId = (world.nextArchetypeId::number)+ 1
|
world.nextArchetypeId = (world.nextArchetypeId :: number) + 1
|
||||||
local id = world.nextArchetypeId
|
local id = world.nextArchetypeId
|
||||||
|
|
||||||
local columns = {} :: { any }
|
local columns = {} :: {any}
|
||||||
|
|
||||||
for _ in types do
|
for _ in types do
|
||||||
table.insert(columns, {})
|
table.insert(columns, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
local archetype = {
|
local archetype = {
|
||||||
id = id,
|
id = id;
|
||||||
types = types,
|
types = types;
|
||||||
type = ty,
|
type = ty;
|
||||||
columns = columns,
|
columns = columns;
|
||||||
entities = {},
|
entities = {};
|
||||||
edges = {},
|
edges = {};
|
||||||
records = {},
|
records = {};
|
||||||
}
|
}
|
||||||
world.archetypeIndex[ty] = archetype
|
world.archetypeIndex[ty] = archetype
|
||||||
world.archetypes[id] = archetype
|
world.archetypes[id] = archetype
|
||||||
|
@ -173,40 +157,40 @@ local World = {}
|
||||||
World.__index = World
|
World.__index = World
|
||||||
function World.new()
|
function World.new()
|
||||||
local self = setmetatable({
|
local self = setmetatable({
|
||||||
entityIndex = {},
|
entityIndex = {};
|
||||||
componentIndex = {},
|
componentIndex = {};
|
||||||
archetypes = {},
|
archetypes = {};
|
||||||
archetypeIndex = {},
|
archetypeIndex = {};
|
||||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
ROOT_ARCHETYPE = (nil :: any) :: Archetype;
|
||||||
nextEntityId = 0,
|
nextEntityId = 0;
|
||||||
nextComponentId = 0,
|
nextComponentId = 0;
|
||||||
nextArchetypeId = 0,
|
nextArchetypeId = 0;
|
||||||
hooks = {
|
hooks = {
|
||||||
[ON_ADD] = {}
|
[ON_ADD] = {};
|
||||||
}
|
};
|
||||||
}, World)
|
}, World)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local function emit(world, eventDescription)
|
local function emit(world, eventDescription)
|
||||||
local event = eventDescription.event
|
local event = eventDescription.event
|
||||||
|
|
||||||
table.insert(world.hooks[event], {
|
table.insert(world.hooks[event], {
|
||||||
ids = eventDescription.ids,
|
ids = eventDescription.ids;
|
||||||
archetype = eventDescription.archetype,
|
archetype = eventDescription.archetype;
|
||||||
otherArchetype = eventDescription.otherArchetype,
|
otherArchetype = eventDescription.otherArchetype;
|
||||||
offset = eventDescription.offset
|
offset = eventDescription.offset;
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
local function onNotifyAdd(world, archetype, otherArchetype, row: number, added: Ty)
|
||||||
if #added > 0 then
|
if #added > 0 then
|
||||||
emit(world, {
|
emit(world, {
|
||||||
event = ON_ADD,
|
event = ON_ADD;
|
||||||
ids = added,
|
ids = added;
|
||||||
archetype = archetype,
|
archetype = archetype;
|
||||||
otherArchetype = otherArchetype,
|
otherArchetype = otherArchetype;
|
||||||
offset = row,
|
offset = row;
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -227,7 +211,7 @@ local function ensureArchetype(world: World, types, prev)
|
||||||
return archetypeOf(world, types, prev)
|
return archetypeOf(world, types, prev)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function findInsert(types: { i53 }, toAdd: i53)
|
local function findInsert(types: {i53}, toAdd: i53)
|
||||||
local count = #types
|
local count = #types
|
||||||
for i = 1, count do
|
for i = 1, count do
|
||||||
local id = types[i]
|
local id = types[i]
|
||||||
|
@ -269,9 +253,9 @@ local function archetypeTraverseAdd(world: World, componentId: i53, from: Archet
|
||||||
if not from then
|
if not from then
|
||||||
-- If there was no source archetype then it should return the ROOT_ARCHETYPE
|
-- If there was no source archetype then it should return the ROOT_ARCHETYPE
|
||||||
if not world.ROOT_ARCHETYPE then
|
if not world.ROOT_ARCHETYPE then
|
||||||
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
local ROOT_ARCHETYPE = archetypeOf(world, {}, nil)
|
||||||
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
|
||||||
end
|
end
|
||||||
from = world.ROOT_ARCHETYPE
|
from = world.ROOT_ARCHETYPE
|
||||||
end
|
end
|
||||||
local edge = ensureEdge(from, componentId)
|
local edge = ensureEdge(from, componentId)
|
||||||
|
@ -314,7 +298,7 @@ function World.set(world: World, entityId: i53, componentId: i53, data: unknown)
|
||||||
if #to.types > 0 then
|
if #to.types > 0 then
|
||||||
-- When there is no previous archetype it should create the archetype
|
-- When there is no previous archetype it should create the archetype
|
||||||
newEntity(entityId, record, to)
|
newEntity(entityId, record, to)
|
||||||
onNotifyAdd(world, to, from, record.row, { componentId })
|
onNotifyAdd(world, to, from, record.row, {componentId})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -326,7 +310,6 @@ local function archetypeTraverseRemove(world: World, componentId: i53, archetype
|
||||||
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
local from = (archetype or world.ROOT_ARCHETYPE) :: Archetype
|
||||||
local edge = ensureEdge(from, componentId)
|
local edge = ensureEdge(from, componentId)
|
||||||
|
|
||||||
|
|
||||||
if not edge.remove then
|
if not edge.remove then
|
||||||
local to = table.clone(from.types)
|
local to = table.clone(from.types)
|
||||||
table.remove(to, table.find(to, componentId))
|
table.remove(to, table.find(to, componentId))
|
||||||
|
@ -347,7 +330,7 @@ function World.remove(world: World, entityId: i53, componentId: i53)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keeping the function as small as possible to enable inlining
|
-- 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 archetype = record.archetype
|
||||||
local archetypeRecord = archetype.records[componentId]
|
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?)
|
function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?)
|
||||||
local id = entityId
|
local id = entityId
|
||||||
local componentIndex = world.componentIndex
|
local componentIndex = world.componentIndex
|
||||||
local record = world.entityIndex[id]
|
local record = world.entityIndex[id]
|
||||||
if not record then
|
if not record then
|
||||||
return nil
|
return nil
|
||||||
|
@ -382,13 +365,12 @@ function World.get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53
|
||||||
end
|
end
|
||||||
|
|
||||||
local function noop(self: Query, ...: i53): () -> (number, ...any)
|
local function noop(self: Query, ...: i53): () -> (number, ...any)
|
||||||
return function()
|
return function() end :: any
|
||||||
end :: any
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local EmptyQuery = {
|
local EmptyQuery = {
|
||||||
__iter = noop,
|
__iter = noop;
|
||||||
without = noop
|
without = noop;
|
||||||
}
|
}
|
||||||
EmptyQuery.__index = EmptyQuery
|
EmptyQuery.__index = EmptyQuery
|
||||||
setmetatable(EmptyQuery, EmptyQuery)
|
setmetatable(EmptyQuery, EmptyQuery)
|
||||||
|
@ -397,7 +379,7 @@ export type Query = typeof(EmptyQuery)
|
||||||
|
|
||||||
function World.query(world: World, ...: i53): Query
|
function World.query(world: World, ...: i53): Query
|
||||||
local compatibleArchetypes = {}
|
local compatibleArchetypes = {}
|
||||||
local components = { ... }
|
local components = {...}
|
||||||
local archetypes = world.archetypes
|
local archetypes = world.archetypes
|
||||||
local queryLength = #components
|
local queryLength = #components
|
||||||
|
|
||||||
|
@ -422,7 +404,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
for id in firstArchetypeMap.sparse do
|
for id in firstArchetypeMap.sparse do
|
||||||
local archetype = archetypes[id]
|
local archetype = archetypes[id]
|
||||||
local archetypeRecords = archetype.records
|
local archetypeRecords = archetype.records
|
||||||
local indices = {}
|
local indices = {}
|
||||||
local skip = false
|
local skip = false
|
||||||
|
|
||||||
for i, componentId in components do
|
for i, componentId in components do
|
||||||
|
@ -437,7 +419,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
if skip then
|
if skip then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
table.insert(compatibleArchetypes, { archetype, indices })
|
table.insert(compatibleArchetypes, {archetype, indices})
|
||||||
end
|
end
|
||||||
|
|
||||||
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
local lastArchetype, compatibleArchetype = next(compatibleArchetypes)
|
||||||
|
@ -449,7 +431,7 @@ function World.query(world: World, ...: i53): Query
|
||||||
preparedQuery.__index = preparedQuery
|
preparedQuery.__index = preparedQuery
|
||||||
|
|
||||||
function preparedQuery:without(...)
|
function preparedQuery:without(...)
|
||||||
local components = { ... }
|
local components = {...}
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
for i = #compatibleArchetypes, 1, -1 do
|
||||||
local archetype = compatibleArchetypes[i][1]
|
local archetype = compatibleArchetypes[i][1]
|
||||||
local shouldRemove = false
|
local shouldRemove = false
|
||||||
|
@ -475,7 +457,6 @@ function World.query(world: World, ...: i53): Query
|
||||||
local lastRow
|
local lastRow
|
||||||
local queryOutput = {}
|
local queryOutput = {}
|
||||||
|
|
||||||
|
|
||||||
function preparedQuery:__iter()
|
function preparedQuery:__iter()
|
||||||
return function()
|
return function()
|
||||||
local archetype = compatibleArchetype[1]
|
local archetype = compatibleArchetype[1]
|
||||||
|
@ -499,16 +480,9 @@ function World.query(world: World, ...: i53): Query
|
||||||
elseif queryLength == 2 then
|
elseif queryLength == 2 then
|
||||||
return entityId, columns[tr[1]][row], columns[tr[2]][row]
|
return entityId, columns[tr[1]][row], columns[tr[2]][row]
|
||||||
elseif queryLength == 3 then
|
elseif queryLength == 3 then
|
||||||
return entityId,
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row]
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row]
|
|
||||||
elseif queryLength == 4 then
|
elseif queryLength == 4 then
|
||||||
return entityId,
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row]
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row],
|
|
||||||
columns[tr[4]][row]
|
|
||||||
elseif queryLength == 5 then
|
elseif queryLength == 5 then
|
||||||
return entityId,
|
return entityId,
|
||||||
columns[tr[1]][row],
|
columns[tr[1]][row],
|
||||||
|
@ -584,7 +558,7 @@ function World.delete(world: World, entityId: i53)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World.observer(world: World, ...)
|
function World.observer(world: World, ...)
|
||||||
local componentIds = { ... }
|
local componentIds = {...}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
event = function(event)
|
event = function(event)
|
||||||
|
@ -628,13 +602,13 @@ function World.observer(world: World, ...)
|
||||||
|
|
||||||
return archetype.entities[row], unpack(queryOutput, 1, #queryOutput)
|
return archetype.entities[row], unpack(queryOutput, 1, #queryOutput)
|
||||||
end
|
end
|
||||||
end
|
end;
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.freeze({
|
return table.freeze({
|
||||||
World = World,
|
World = World;
|
||||||
ON_ADD = ON_ADD,
|
ON_ADD = ON_ADD;
|
||||||
ON_REMOVE = ON_REMOVE,
|
ON_REMOVE = ON_REMOVE;
|
||||||
ON_SET = ON_SET
|
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"
|
TestEZ = "roblox/testez@0.4.1"
|
||||||
Matter = "matter-ecs/matter@0.8.0"
|
Matter = "matter-ecs/matter@0.8.0"
|
||||||
ecr = "centau/ecr@0.8.0"
|
ecr = "centau/ecr@0.8.0"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue