Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Revert "Revert "Port changes from archetype negation branch""
Browse files Browse the repository at this point in the history
This reverts commit 9cd23e5.
  • Loading branch information
Ukendio committed Dec 24, 2023
1 parent 9cd23e5 commit 5009e3b
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 205 deletions.
2 changes: 1 addition & 1 deletion lib/World.lua
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ function World:query(...)
self:_markStorageDirty()
end

return QueryResult.new(self, expand, archetype, compatibleArchetypes)
return QueryResult.new(self, expand, compatibleArchetypes)
end

local function cleanupQueryChanged(hookState)
Expand Down
88 changes: 0 additions & 88 deletions lib/World.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,6 @@ return function()
expect(world:size()).to.equal(1)
end)

it("without prototype", function()
local world = World.new()

local Hello = component()
local Bob = component()
local Shirley = component()

local _helloBob = world:spawn(Hello(), Bob())
local _helloShirley = world:spawn(Hello(), Shirley())

local withoutCount = 0
for _ in world:query(Hello):without(Bob) do
withoutCount += 1
end

expect(withoutCount).to.equal(1)
end)

it("should be queryable", function()
local world = World.new()

Expand Down Expand Up @@ -544,76 +526,6 @@ return function()
expect(#world:query(Player):without(Poison):snapshot()).to.equal(1)
end)

it("should allow viewing a query", function()
local Parent = component("Parent")
local Transform = component("Transform")
local Root = component("Root")

local world = World.new()

local root = world:spawn(Transform({ pos = Vector2.new(3, 4) }), Root())
local _otherRoot = world:spawn(Transform({ pos = Vector2.new(1, 2) }), Root())

local child = world:spawn(
Parent({
entity = root,
fromChild = Transform({ pos = Vector2.one }),
}),
Transform.new({ pos = Vector2.zero })
)

local _otherChild = world:spawn(
Parent({
entity = root,
fromChild = Transform({ pos = Vector2.new(0, 0) }),
}),
Transform.new({ pos = Vector2.zero })
)

local _grandChild = world:spawn(
Parent({
entity = child,
fromChild = Transform({ pos = Vector2.new(-1, 0) }),
}),
Transform.new({ pos = Vector2.zero })
)

local parents = world:query(Parent):view()
local roots = world:query(Transform, Root):view()

expect(parents:contains(root)).to.equal(false)

local orderOfIteration = {}

for id in world:query(Transform, Parent) do
table.insert(orderOfIteration, id)
end

local view = world:query(Transform, Parent):view()
local i = 0
for id in view do
i += 1
expect(orderOfIteration[i]).to.equal(id)
end

for id, absolute, parent in world:query(Transform, Parent) do
local relative = parent.fromChild.pos
local ancestor = parent.entity
local current = parents:get(ancestor)
while current do
relative = current.fromChild.pos * relative
ancestor = current.entity
current = parents:get(ancestor)
end

local pos = roots:get(ancestor).pos

world:insert(id, absolute:patch({ pos = Vector2.new(pos.x + relative.x, pos.y + relative.y) }))
end

expect(world:get(child, Transform).pos).to.equal(Vector2.new(4, 5))
end)

it("should not invalidate iterators", function()
local world = World.new()
local A = component()
Expand Down
125 changes: 9 additions & 116 deletions lib/query.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
local archetypeModule = require(script.Parent.archetype)
local archetypeOf = archetypeModule.archetypeOf

--[=[
@class QueryResult
Expand All @@ -18,15 +15,15 @@ local archetypeOf = archetypeModule.archetypeOf
local QueryResult = {}
QueryResult.__index = QueryResult

function QueryResult.new(world, expand, queryArchetype, compatibleArchetypes)
function QueryResult.new(world, expand, compatibleArchetypes)
return setmetatable({
world = world,
seenEntities = {},
currentCompatibleArchetype = next(compatibleArchetypes),
compatibleArchetypes = compatibleArchetypes,
storageIndex = 1,
_filter = {},
_expand = expand,
_queryArchetype = queryArchetype,
}, QueryResult)
end

Expand Down Expand Up @@ -81,6 +78,12 @@ local function nextItem(query)

seenEntities[entityId] = true

for _, metatable in query._filter do
if entityData[metatable] then
return nextItem(query)
end
end

return entityId, entityData
end

Expand Down Expand Up @@ -204,119 +207,9 @@ end
]=]

function QueryResult:without(...)
local world = self.world
local negativeArchetype = `{self._queryArchetype}||{archetypeOf(...)}`
self._filter = { ... }

if world._queryCache[negativeArchetype] == nil then
world:_newQueryArchetype(negativeArchetype)
end

local compatibleArchetypes = world._queryCache[negativeArchetype]

self.compatibleArchetypes = compatibleArchetypes
self.currentCompatibleArchetype = next(compatibleArchetypes)
return self
end

--[=[
@class View
Provides random access to the results of a query.
Calling the table is equivalent iterating a query.
```lua
for id, player, health, poison in world:query(Player, Health, Poison):view() do
-- Do something
end
```
]=]

local View = {}
View.__index = View

function View.new()
return setmetatable({
fetches = {},
}, View)
end

function View:__iter()
local current = self.head
return function()
if current then
local entity = current.entity
local fetch = self.fetches[entity]
current = current.next

return entity, unpack(fetch, 1, fetch.n)
end
end
end

--[=[
Retrieve the query results to corresponding `entity`
@param entity number - the entity ID
@return ...ComponentInstance
]=]
function View:get(entity)
if not self:contains(entity) then
return
end

local fetch = self.fetches[entity]

return unpack(fetch, 1, fetch.n)
end

--[=[
Equivalent to `world:contains()`
@param entity number - the entity ID
@return boolean
]=]

function View:contains(entity)
return self.fetches[entity] ~= nil
end

--[=[
Creates a View of the query and does all of the iterator tasks at once at an amortized cost.
This is used for many repeated random access to an entity. If you only need to iterate, just use a query.
```lua
for id, player, health, poison in world:query(Player, Health, Poison):view() do
-- Do something
end
local dyingPeople = world:query(Player, Health, Poison):view()
local remainingHealth = dyingPeople:get(entity)
```
@param ... Component - The component types to query. Only entities with *all* of these components will be returned.
@return View See [View](/api/View) docs.
]=]

function QueryResult:view()
local function iter()
return nextItem(self)
end

local view = View.new()

for entityId, entityData in iter do
if entityId then
-- We start at 2 on Select since we don't need want to pack the entity id.
local fetch = table.pack(select(2, self._expand(entityId, entityData)))
local node = { entity = entityId, next = nil }
view.fetches[entityId] = fetch
if not view.head then
view.head = node
else
local current = view.head
while current.next do
current = current.next
end
current.next = node
end
end
end

return view
end

return QueryResult

0 comments on commit 5009e3b

Please sign in to comment.