Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/src/client/systems/roombasHurt.luau
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local Matter = require(ReplicatedStorage.Lib.Matter)

local function roombasHurt(world)
for _, _, model in world:query(Components.Roomba, Components.Model) do
for _, part in Matter.useEvent(model.model.PrimaryPart, "Touched") do
for _, part in Matter.useEvent(model.PrimaryPart, "Touched") do
local touchedModel = part:FindFirstAncestorWhichIsA("Model")
if not touchedModel then
continue
Expand Down
6 changes: 3 additions & 3 deletions example/src/client/systems/spinSpinners.luau
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ local function spinSpinners(world, _, ui)
local randomize = ui.button("Randomize colors!"):clicked()

for _, model in world:query(Components.Model, Components.Spinner) do
model.model.PrimaryPart.CFrame = model.model.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(5), 0)
model.model.PrimaryPart.Transparency = transparency
model.PrimaryPart.CFrame = model.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(5), 0)
model.PrimaryPart.Transparency = transparency

if randomize then
model.model.PrimaryPart.BrickColor = BrickColor.random()
model.PrimaryPart.BrickColor = BrickColor.random()
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion example/src/server/systems/mothershipsSpawnRoombas.luau
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ local function mothershipsSpawnRoombas(world)
for id, model, lasering, transform in
world:query(Components.Model, Components.Lasering, Components.Transform, Components.Mothership)
do
model.model.Beam.Transparency = 1 - lasering.remainingTime
model.Beam.Transparency = 1 - lasering.remainingTime

lasering = lasering:patch({
remainingTime = lasering.remainingTime - Matter.useDeltaTime(),
Expand Down
7 changes: 1 addition & 6 deletions example/src/server/systems/playersAreTargets.luau
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ local Matter = require(ReplicatedStorage.Lib.Matter)
local function playersAreTargets(world)
for _, player in ipairs(Players:GetPlayers()) do
for _, character in Matter.useEvent(player, "CharacterAdded") do
world:spawn(
Components.Target(),
Components.Model({
model = character,
})
)
world:spawn(Components.Target(), Components.Model(character))
end
end

Expand Down
10 changes: 5 additions & 5 deletions example/src/server/systems/removeMissingModels.luau
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ local Matter = require(ReplicatedStorage.Lib.Matter)

local function removeMissingModels(world)
for id, model in world:query(Components.Model) do
for _ in Matter.useEvent(model.model, "AncestryChanged") do
if model.model:IsDescendantOf(game) == false then
for _ in Matter.useEvent(model, "AncestryChanged") do
if model:IsDescendantOf(game) == false then
world:remove(id, Components.Model)
break
end
end
if not model.model.PrimaryPart then
if not model.PrimaryPart then
world:remove(id, Components.Model)
end
end

for _id, modelRecord in world:queryChanged(Components.Model) do
if modelRecord.new == nil then
if modelRecord.old and modelRecord.old.model then
modelRecord.old.model:Destroy()
if modelRecord.old then
modelRecord.old:Destroy()
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions example/src/server/systems/roombasMove.luau
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ local Components = require(ReplicatedStorage.Shared.components)
local function roombasMove(world)
local targets = {}
for _, model in world:query(Components.Model, Components.Target) do
table.insert(targets, model.model.PrimaryPart.CFrame.p)
table.insert(targets, model.PrimaryPart.CFrame.p)
end

for _, _, charge, model in world:query(Components.Roomba, Components.Charge, Components.Model) do
Expand All @@ -13,7 +13,7 @@ local function roombasMove(world)
end

local closestPosition, closestDistance
local currentPosition = model.model.PrimaryPart.CFrame.p
local currentPosition = model.PrimaryPart.CFrame.p

for _, target in ipairs(targets) do
local distance = (currentPosition - target).magnitude
Expand All @@ -24,7 +24,7 @@ local function roombasMove(world)
end

if closestPosition then
local body = model.model.Roomba
local body = model.Roomba
local force = body:GetMass() * 20

if closestDistance < 4 then
Expand Down
9 changes: 2 additions & 7 deletions example/src/server/systems/spawnMotherships.luau
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,7 @@ local function spawnMotherships(world)
model.Parent = workspace
model.PrimaryPart:SetNetworkOwner(nil)

world:insert(
id,
Components.Model({
model = model,
})
)
world:insert(id, Components.Model(model))
end

for id, mothership, transform in
Expand All @@ -58,7 +53,7 @@ local function spawnMotherships(world)
end

for _, mothership, model in world:query(Components.Mothership, Components.Model):without(Components.Lasering) do
model.model.Roomba.AlignPosition.Position = mothership.goal
model.Roomba.AlignPosition.Position = mothership.goal
end
end

Expand Down
7 changes: 1 addition & 6 deletions example/src/server/systems/spawnRoombas.luau
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ local function spawnRoombas(world)
model.Parent = workspace
model.PrimaryPart:SetNetworkOwner(nil)

world:insert(
id,
Components.Model({
model = model,
})
)
world:insert(id, Components.Model(model))
end
end

Expand Down
8 changes: 4 additions & 4 deletions example/src/server/systems/updateTransforms.luau
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ local function updateTransforms(world)
end

if transformRecord.new and not transformRecord.new.doNotReconcile then
model.model:SetPrimaryPartCFrame(transformRecord.new.cframe)
model:SetPrimaryPartCFrame(transformRecord.new.cframe)
end
end

Expand All @@ -33,18 +33,18 @@ local function updateTransforms(world)
end

if modelRecord.new then
modelRecord.new.model:SetPrimaryPartCFrame(transform.cframe)
modelRecord.new:SetPrimaryPartCFrame(transform.cframe)
end
end

-- Update Transform on unanchored Models
for id, model, transform in world:query(Components.Model, Components.Transform) do
if model.model.PrimaryPart.Anchored then
if model.PrimaryPart.Anchored then
continue
end

local existingCFrame = transform.cframe
local currentCFrame = model.model.PrimaryPart.CFrame
local currentCFrame = model.PrimaryPart.CFrame

-- Despawn models that fall into the void
if currentCFrame.Y < -400 then
Expand Down
4 changes: 1 addition & 3 deletions example/src/shared/setupTags.luau
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ local function setupTags(world)
local function spawnBound(instance, component)
local id = world:spawn(
component(),
Components.Model({
model = instance,
}),
Components.Model(instance),
Components.Transform({
cframe = instance.PrimaryPart.CFrame,
})
Expand Down
3 changes: 1 addition & 2 deletions example/src/shared/start.luau
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ local function start(containers)
end

local model = world:get(id, components.Model)

return model and model.model or nil
return model
end

local loop = Matter.Loop.new(world, state, debugger:getWidgets())
Expand Down
2 changes: 1 addition & 1 deletion example/src/shared/systems/updateModelAttribute.luau
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ local name = RunService:IsServer() and "serverEntityId" or "clientEntityId"
local function updateModelAttribute(world)
for id, record in world:queryChanged(Components.Model) do
if record.new then
record.new.model:SetAttribute(name, id)
record.new:SetAttribute(name, id)
end
end
end
Expand Down
167 changes: 163 additions & 4 deletions lib/Archetype.luau
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,53 @@ export type ComponentIds = { ComponentId }
export type Component = { [any]: any }
export type ComponentInstance = { [any]: any }

type EdgeId = ComponentId
-- type EdgeDiff = {
-- added: {},
-- removed: {},
-- }

type GraphEdge = {
id: EdgeId,

-- Source archetype
from: Archetype?,

-- Destination archetype
to: Archetype?,

-- -- What changes on the edge
-- diff: EdgeDiff,

prev: GraphEdge,
next: GraphEdge,
}

type GraphEdges = { [EdgeId]: GraphEdge }
type GraphNode = {
outgoing: { additive: GraphEdges, subtractive: GraphEdges },

incoming: { additive: GraphEdge?, subtractive: GraphEdge? },
}

export type ArchetypeId = string
export type Archetype = {
entities: { EntityId },
componentIds: { ComponentId },
idToIndex: { [ComponentId]: number },
indexToId: { [number]: ComponentId },
fields: { { ComponentInstance } },
node: GraphNode,
}

function hash(componentIds: { number })
export type Archetypes = { [ArchetypeId]: Archetype? }

local function hash(componentIds: { number })
table.sort(componentIds)
return table.concat(componentIds, "_")
end

function new(componentIds: { ComponentId }): (Archetype, ArchetypeId)
local function new(componentIds: { ComponentId }): (Archetype, ArchetypeId)
local length = #componentIds
local archetypeId = hash(componentIds)

Expand All @@ -32,6 +64,13 @@ function new(componentIds: { ComponentId }): (Archetype, ArchetypeId)
idToIndex = idToIndex,
indexToId = indexToId,
fields = fields,
node = {
outgoing = {
additive = {},
subtractive = {},
},
incoming = {},
},
}

for index, componentId in componentIds do
Expand All @@ -44,7 +83,127 @@ function new(componentIds: { ComponentId }): (Archetype, ArchetypeId)
return archetype, archetypeId
end

return {
local function ensureArchetype(archetypes: Archetypes, componentArchetypeMap, componentIds: { ComponentId })
local archetypeId = hash(componentIds)
local existingArchetype = archetypes[archetypeId]
if existingArchetype then
return existingArchetype
end

-- Create new archetype
local archetype, archetypeId = new(componentIds)
archetypes[archetypeId] = archetype

for _, componentId in componentIds do
local associatedArchetypes = componentArchetypeMap[componentId]
if associatedArchetypes == nil then
associatedArchetypes = {}
componentArchetypeMap[componentId] = associatedArchetypes
end

table.insert(associatedArchetypes, archetype)
end

return archetype
end

--- Create or return the edge for a given id
local function ensureEdge(edges: GraphEdges, componentId: ComponentId): GraphEdge
local edge = edges[componentId]
if edge == nil then
edge = {}
edges[componentId] = edge
end

return edge
end

local function initializeEdge(archetype: Archetype, nextArchetype: Archetype, componentId: ComponentId, edge: GraphEdge)
edge.from = archetype
edge.to = nextArchetype
edge.id = componentId
end

--- Traverse the graph to find the next archetype with the new component. This will create the archetype if it
--- doesn't exist and create the edge if it doesn't exist.
local function traverseAdd(
archetypes: Archetypes,
archetype: Archetype,
componentArchetypeMap,
componentId: ComponentId
): Archetype
local node = archetype.node
local edge = ensureEdge(node.outgoing.additive, componentId)
local to = edge.to
if to then
return to
end

-- Get the archetype
local nextComponentIds = table.clone(archetype.componentIds)
table.insert(nextComponentIds, componentId)
table.sort(nextComponentIds)

local nextArchetype = ensureArchetype(archetypes, componentArchetypeMap, nextComponentIds)
initializeEdge(archetype, nextArchetype, componentId, edge)

-- Add new incoming to the list
local nextIncoming = nextArchetype.node.incoming
local link = nextIncoming.additive

nextIncoming.additive = edge
edge.prev = nextIncoming

edge.next = link
if link then
link.prev = edge
end

return edge.to
end

local function traverseRemove(
archetypes: Archetypes,
archetype: Archetype,
componentArchetypeMap,
componentId: ComponentId
)
local node = archetype.node
local edge = ensureEdge(node.outgoing.subtractive, componentId)
local to = edge.to
if to then
return to
end

-- Get the archetype
local nextComponentIds = table.clone(archetype.componentIds)
local index = table.find(nextComponentIds, componentId)
if index then
table.remove(nextComponentIds, index)
end

local nextArchetype = ensureArchetype(archetypes, componentArchetypeMap, nextComponentIds)
initializeEdge(archetype, nextArchetype, componentId, edge)

-- Add new incoming to the list
local nextIncoming = nextArchetype.node.incoming
local link = nextIncoming.subtractive

nextIncoming.subtractive = edge
edge.next = nextIncoming

edge.prev = link
if link then
link.prev = edge
end

return edge.to
end

return table.freeze({
new = new,
hash = hash,
}
ensureArchetype = ensureArchetype,
traverseAdd = traverseAdd,
traverseRemove = traverseRemove,
})
Loading