mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-08-20 21:38:29 -04:00
Replaced Pools with Filters
Filters allow for a Pool constructor (defaults to Lists) that can be used to define Custom Pools. The constructor is a function that takes the Filter Definition and returns a Custom Pool with these functions: :add(e) - Add the Entity to the pool :remove(e) - Remove the Entity from the pool :has(e) boolean - Checks if the Entity exists in the pool :clear() - Clears the Pool from Entities Fixes #40
This commit is contained in:
parent
07bd5d0f28
commit
743d662ef9
6 changed files with 221 additions and 163 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -38,3 +38,6 @@ luac.out
|
||||||
*.i*86
|
*.i*86
|
||||||
*.x86_64
|
*.x86_64
|
||||||
*.hex
|
*.hex
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
168
concord/filter.lua
Normal file
168
concord/filter.lua
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
--- Used to filter Entities with specific Components
|
||||||
|
-- A Filter has an associated Pool that can contain any amount of Entities.
|
||||||
|
-- @classmod Filter
|
||||||
|
|
||||||
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
||||||
|
|
||||||
|
local List = require(PATH..".list")
|
||||||
|
local Type = require(PATH..".type")
|
||||||
|
local Components = require(PATH..".components")
|
||||||
|
|
||||||
|
|
||||||
|
local Filter = {}
|
||||||
|
Filter.__mt = {
|
||||||
|
__index = Filter,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Validates a Filter Definition to make sure every component is valid.
|
||||||
|
-- @string name Name for the Filter.
|
||||||
|
-- @tparam table definition Table containing the Filter Definition
|
||||||
|
-- @tparam onComponent Optional function, called when a component is valid.
|
||||||
|
function Filter.validate (name, def, onComponent)
|
||||||
|
if type(def) ~= 'table' then
|
||||||
|
error("invalid component list for filter '"..name.."' (table expected, got "..type(def)..")", 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not onComponent and def.constructor and not Type.isCallable(def.constructor) then
|
||||||
|
error("invalid pool constructor (callable expected, got "..type(def.constructor)..")", 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
for n, component in ipairs(def) do
|
||||||
|
local ok, err, reject = Components.try(component, true)
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
error("invalid component for filter '"..name.."' at position #"..n.." ("..err..")", 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
if onComponent then
|
||||||
|
onComponent(component, reject)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Parses the Filter Defintion into two tables
|
||||||
|
-- required: An array of all the required component names.
|
||||||
|
-- rejected: An array of all the components that will be rejected.
|
||||||
|
-- @string name Name for the Filter.
|
||||||
|
-- @tparam table definition Table containing the Filter Definition
|
||||||
|
-- @treturn table required
|
||||||
|
-- @treturn table rejected
|
||||||
|
function Filter.parse (name, def)
|
||||||
|
local required, rejected = {}, {}
|
||||||
|
|
||||||
|
Filter.validate(name, def, function (component, reject)
|
||||||
|
if reject then
|
||||||
|
table.insert(rejected, reject)
|
||||||
|
else
|
||||||
|
table.insert(required, component)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return required, rejected
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a new Filter
|
||||||
|
-- @string name Name for the Filter.
|
||||||
|
-- @tparam table definition Table containing the Filter Definition
|
||||||
|
-- @treturn Filter The new Filter
|
||||||
|
-- @treturn Pool The associated Pool
|
||||||
|
function Filter.new (name, def)
|
||||||
|
local constructor = def.constructor or List
|
||||||
|
local pool = constructor(def)
|
||||||
|
|
||||||
|
local required, rejected = Filter.parse(name, def)
|
||||||
|
|
||||||
|
local filter = setmetatable({
|
||||||
|
pool = pool,
|
||||||
|
|
||||||
|
__required = required,
|
||||||
|
__rejected = rejected,
|
||||||
|
|
||||||
|
__name = name,
|
||||||
|
|
||||||
|
__isFilter = true,
|
||||||
|
}, Filter.__mt)
|
||||||
|
|
||||||
|
return filter, pool
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks if an Entity fulfills the Filter requirements.
|
||||||
|
-- @tparam Entity e Entity to check
|
||||||
|
-- @treturn boolean
|
||||||
|
function Filter:eligible(e)
|
||||||
|
for i=#self.__required, 1, -1 do
|
||||||
|
local name = self.__required[i]
|
||||||
|
if not e[name] then return false end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=#self.__rejected, 1, -1 do
|
||||||
|
local name = self.__rejected[i]
|
||||||
|
if e[name] then return false end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Filter:evaluate (e)
|
||||||
|
local has = self.pool:has(e)
|
||||||
|
local eligible = self:eligible(e)
|
||||||
|
|
||||||
|
if not has and eligible then
|
||||||
|
self.pool:add(e)
|
||||||
|
elseif has and not eligible then
|
||||||
|
self.pool:remove(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Adds an Entity to the Pool, if it passes the Filter.
|
||||||
|
-- @param e Entity to add
|
||||||
|
-- @param bypass Whether to bypass the Filter or not.
|
||||||
|
-- @treturn Filter self
|
||||||
|
-- @treturn boolean Whether the entity was added or not.
|
||||||
|
function Filter:add (e, bypass)
|
||||||
|
if not bypass and not self:eligible(e) then
|
||||||
|
return self, false
|
||||||
|
end
|
||||||
|
|
||||||
|
self.pool:add(e)
|
||||||
|
|
||||||
|
return self, true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove an Entity from the Pool associated to this Filter.
|
||||||
|
-- @param e Entity to remove
|
||||||
|
-- @treturn Filter self
|
||||||
|
function Filter:remove (e)
|
||||||
|
self.pool:remove(e)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear the Pool associated to this Filter.
|
||||||
|
-- @param e Entity to remove
|
||||||
|
-- @treturn Filter self
|
||||||
|
function Filter:clear (e)
|
||||||
|
self.pool:clear(e)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if the Pool bound to this System contains the passed Entity
|
||||||
|
-- @param e Entity to check
|
||||||
|
-- @treturn boolean Whether the Entity exists.
|
||||||
|
function Filter:has (e)
|
||||||
|
return self.pool:has(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the name of the Filter
|
||||||
|
-- @treturn string
|
||||||
|
function Filter:getName()
|
||||||
|
return self.__name
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(Filter, {
|
||||||
|
__call = function(_, ...)
|
||||||
|
return Filter.new(...)
|
||||||
|
end,
|
||||||
|
})
|
|
@ -16,7 +16,7 @@ end
|
||||||
|
|
||||||
--- Adds an object to the List.
|
--- Adds an object to the List.
|
||||||
-- Object must be of reference type
|
-- Object must be of reference type
|
||||||
-- Object may not be the string 'size'
|
-- Object may not be the string 'size', 'onAdded' or 'onRemoved'
|
||||||
-- @param obj Object to add
|
-- @param obj Object to add
|
||||||
-- @treturn List self
|
-- @treturn List self
|
||||||
function List:add(obj)
|
function List:add(obj)
|
||||||
|
@ -26,6 +26,7 @@ function List:add(obj)
|
||||||
self[obj] = size
|
self[obj] = size
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
|
if self.onAdded then self:onAdded(obj) end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ function List:remove(obj)
|
||||||
self[obj] = nil
|
self[obj] = nil
|
||||||
self.size = size - 1
|
self.size = size - 1
|
||||||
|
|
||||||
|
if self.onRemoved then self:onRemoved(obj) end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,6 +110,16 @@ function List:sort(order)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Callback for when an item is added to the List.
|
||||||
|
-- @param obj Object that was added
|
||||||
|
function List:onAdded (obj) --luacheck: ignore
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Callback for when an item is removed to the List.
|
||||||
|
-- @param obj Object that was removed
|
||||||
|
function List:onRemoved (obj) --luacheck: ignore
|
||||||
|
end
|
||||||
|
|
||||||
return setmetatable(List, {
|
return setmetatable(List, {
|
||||||
__call = function()
|
__call = function()
|
||||||
return List.new()
|
return List.new()
|
||||||
|
|
115
concord/pool.lua
115
concord/pool.lua
|
@ -1,115 +0,0 @@
|
||||||
--- Used to iterate over Entities with a specific Components
|
|
||||||
-- A Pool contain a any amount of Entities.
|
|
||||||
-- @classmod Pool
|
|
||||||
|
|
||||||
local PATH = (...):gsub('%.[^%.]+$', '')
|
|
||||||
|
|
||||||
local List = require(PATH..".list")
|
|
||||||
|
|
||||||
local Pool = {}
|
|
||||||
Pool.__mt = {
|
|
||||||
__index = Pool,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Creates a new Pool
|
|
||||||
-- @string name Name for the Pool.
|
|
||||||
-- @tparam table filter Table containing the required BaseComponents
|
|
||||||
-- @treturn Pool The new Pool
|
|
||||||
function Pool.new(name, filter)
|
|
||||||
local pool = setmetatable(List(), Pool.__mt)
|
|
||||||
|
|
||||||
pool.__name = name
|
|
||||||
pool.__filter = filter
|
|
||||||
|
|
||||||
pool.__isPool = true
|
|
||||||
|
|
||||||
return pool
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Checks if an Entity is eligible for the Pool.
|
|
||||||
-- @tparam Entity e Entity to check
|
|
||||||
-- @treturn boolean
|
|
||||||
function Pool:eligible(e)
|
|
||||||
for i=#self.__filter.require, 1, -1 do
|
|
||||||
local name = self.__filter.require[i]
|
|
||||||
if not e[name] then return false end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=#self.__filter.reject, 1, -1 do
|
|
||||||
local name = self.__filter.reject[i]
|
|
||||||
if e[name] then return false end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Adds an Entity to the Pool, if it can be eligible.
|
|
||||||
-- @param e Entity to add
|
|
||||||
-- @treturn Pool self
|
|
||||||
-- @treturn boolean Whether the entity was added or not
|
|
||||||
function Pool:add(e, bypass)
|
|
||||||
if not bypass and not self:eligible(e) then
|
|
||||||
return self, false
|
|
||||||
end
|
|
||||||
|
|
||||||
List.add(self, e)
|
|
||||||
self:onEntityAdded(e)
|
|
||||||
|
|
||||||
return self, true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove an Entity from the Pool.
|
|
||||||
-- @param e Entity to remove
|
|
||||||
-- @treturn Pool self
|
|
||||||
function Pool:remove(e)
|
|
||||||
List.remove(self, e)
|
|
||||||
self:onEntityRemoved(e)
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Evaluate whether an Entity should be added or removed from the Pool.
|
|
||||||
-- @param e Entity to add or remove
|
|
||||||
-- @treturn Pool self
|
|
||||||
function Pool:evaluate(e)
|
|
||||||
local has = self:has(e)
|
|
||||||
local eligible = self:eligible(e)
|
|
||||||
|
|
||||||
if not has and eligible then
|
|
||||||
self:add(e, true) --Bypass the check cause we already checked
|
|
||||||
elseif has and not eligible then
|
|
||||||
self:remove(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Gets the name of the Pool
|
|
||||||
-- @treturn string
|
|
||||||
function Pool:getName()
|
|
||||||
return self.__name
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Gets the filter of the Pool.
|
|
||||||
-- Warning: Do not modify this filter.
|
|
||||||
-- @return Filter of the Pool.
|
|
||||||
function Pool:getFilter()
|
|
||||||
return self.__filter
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Callback for when an Entity is added to the Pool.
|
|
||||||
-- @tparam Entity e Entity that was added.
|
|
||||||
function Pool:onEntityAdded(e) -- luacheck: ignore
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Callback for when an Entity is removed from the Pool.
|
|
||||||
-- @tparam Entity e Entity that was removed.
|
|
||||||
function Pool:onEntityRemoved(e) -- luacheck: ignore
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable(Pool, {
|
|
||||||
__index = List,
|
|
||||||
__call = function(_, ...)
|
|
||||||
return Pool.new(...)
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -5,9 +5,8 @@
|
||||||
|
|
||||||
local PATH = (...):gsub('%.[^%.]+$', '')
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
||||||
|
|
||||||
local Pool = require(PATH..".pool")
|
local Filter = require(PATH..".filter")
|
||||||
local Utils = require(PATH..".utils")
|
local Utils = require(PATH..".utils")
|
||||||
local Components = require(PATH..".components")
|
|
||||||
|
|
||||||
local System = {
|
local System = {
|
||||||
ENABLE_OPTIMIZATION = true,
|
ENABLE_OPTIMIZATION = true,
|
||||||
|
@ -19,7 +18,7 @@ System.mt = {
|
||||||
local system = setmetatable({
|
local system = setmetatable({
|
||||||
__enabled = true,
|
__enabled = true,
|
||||||
|
|
||||||
__pools = {},
|
__filters = {},
|
||||||
__world = world,
|
__world = world,
|
||||||
|
|
||||||
__isSystem = true,
|
__isSystem = true,
|
||||||
|
@ -33,11 +32,11 @@ System.mt = {
|
||||||
Utils.shallowCopy(systemClass, system)
|
Utils.shallowCopy(systemClass, system)
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, filter in pairs(systemClass.__filters) do
|
for name, def in pairs(systemClass.__definition) do
|
||||||
local pool = Pool(name, filter)
|
local filter, pool = Filter(name, Utils.shallowCopy(def, {}))
|
||||||
|
|
||||||
system[name] = pool
|
system[name] = pool
|
||||||
system.__pools[#system.__pools + 1] = pool
|
table.insert(system.__filters, filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
system:init(world)
|
system:init(world)
|
||||||
|
@ -45,45 +44,20 @@ System.mt = {
|
||||||
return system
|
return system
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
local validateFilters = function (definition)
|
|
||||||
local filters = {}
|
|
||||||
|
|
||||||
for name, componentsList in pairs(definition) do
|
|
||||||
if type(name) ~= 'string' then
|
|
||||||
error("invalid name for filter (string key expected, got "..type(name)..")", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(componentsList) ~= 'table' then
|
|
||||||
error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
filters[name] = { require = {}, reject = {} }
|
|
||||||
|
|
||||||
for n, component in ipairs(componentsList) do
|
|
||||||
local ok, componentClass, rejected = Components.try(component, true)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
error("invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3)
|
|
||||||
elseif rejected then
|
|
||||||
table.insert(filters[name].reject, rejected)
|
|
||||||
else
|
|
||||||
table.insert(filters[name].require, component)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return filters
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Creates a new SystemClass.
|
--- Creates a new SystemClass.
|
||||||
-- @param table filters A table containing filters (name = {components...})
|
-- @param table filters A table containing filters (name = {components...})
|
||||||
-- @treturn System A new SystemClass
|
-- @treturn System A new SystemClass
|
||||||
function System.new(definition)
|
function System.new(definition)
|
||||||
local filters = validateFilters(definition)
|
for name, def in pairs(definition) do
|
||||||
|
if type(name) ~= 'string' then
|
||||||
|
error("invalid name for filter (string key expected, got "..type(name)..")", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
Filter.validate(name, def)
|
||||||
|
end
|
||||||
|
|
||||||
local systemClass = setmetatable({
|
local systemClass = setmetatable({
|
||||||
__filters = filters,
|
__definition = definition,
|
||||||
|
|
||||||
__isSystemClass = true,
|
__isSystemClass = true,
|
||||||
}, System.mt)
|
}, System.mt)
|
||||||
|
@ -103,8 +77,8 @@ end
|
||||||
-- @param e The Entity to check
|
-- @param e The Entity to check
|
||||||
-- @treturn System self
|
-- @treturn System self
|
||||||
function System:__evaluate(e)
|
function System:__evaluate(e)
|
||||||
for _, pool in ipairs(self.__pools) do
|
for _, filter in ipairs(self.__filters) do
|
||||||
pool:evaluate(e)
|
filter:evaluate(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -114,9 +88,9 @@ end
|
||||||
-- @param e The Entity to remove
|
-- @param e The Entity to remove
|
||||||
-- @treturn System self
|
-- @treturn System self
|
||||||
function System:__remove(e)
|
function System:__remove(e)
|
||||||
for _, pool in ipairs(self.__pools) do
|
for _, filter in ipairs(self.__filters) do
|
||||||
if pool:has(e) then
|
if filter:has(e) then
|
||||||
pool:remove(e)
|
filter:remove(e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -126,8 +100,8 @@ end
|
||||||
-- Internal: Clears all Entities from the System.
|
-- Internal: Clears all Entities from the System.
|
||||||
-- @treturn System self
|
-- @treturn System self
|
||||||
function System:__clear()
|
function System:__clear()
|
||||||
for i = 1, #self.__pools do
|
for _, filter in ipairs(self.__filters) do
|
||||||
self.__pools[i]:clear()
|
filter:clear()
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -3,6 +3,15 @@
|
||||||
|
|
||||||
local Type = {}
|
local Type = {}
|
||||||
|
|
||||||
|
function Type.isCallable(t)
|
||||||
|
if type(t) == "function" then return true end
|
||||||
|
|
||||||
|
local meta = getmetatable(t)
|
||||||
|
if meta and type(meta.__call) == "function" then return true end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns if object is an Entity.
|
--- Returns if object is an Entity.
|
||||||
-- @param t Object to check
|
-- @param t Object to check
|
||||||
-- @treturn boolean
|
-- @treturn boolean
|
||||||
|
@ -45,4 +54,11 @@ function Type.isWorld(t)
|
||||||
return type(t) == "table" and t.__isWorld or false
|
return type(t) == "table" and t.__isWorld or false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns if object is a Filter.
|
||||||
|
-- @param t Object to check
|
||||||
|
-- @treturn boolean
|
||||||
|
function Type.isFilter(t)
|
||||||
|
return type(t) == "table" and t.__isFilter or false
|
||||||
|
end
|
||||||
|
|
||||||
return Type
|
return Type
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue