From c640641b0916b2e996a4c960f6115910b3b1d63c Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 07:45:25 -0300 Subject: [PATCH] CONCORD IS DEAD Long live Concord!! --- concord/assemblage.lua | 49 -------------- concord/assemblages.lua | 49 -------------- concord/component.lua | 18 ++++- concord/components.lua | 45 ++++++++----- concord/entity.lua | 80 ++++++++++++---------- concord/init.lua | 36 +++++----- concord/list.lua | 6 +- concord/pool.lua | 47 +++++++++---- concord/system.lua | 110 +++++++++++++------------------ concord/systems.lua | 48 -------------- concord/type.lua | 7 -- concord/world.lua | 54 ++++++++------- concord/worlds.lua | 48 -------------- examples/assemblageTest/init.lua | 38 +++++------ examples/serialization/init.lua | 14 ++-- examples/simpleDrawing/init.lua | 38 +++++------ 16 files changed, 263 insertions(+), 424 deletions(-) delete mode 100644 concord/assemblage.lua delete mode 100644 concord/assemblages.lua delete mode 100644 concord/systems.lua delete mode 100644 concord/worlds.lua diff --git a/concord/assemblage.lua b/concord/assemblage.lua deleted file mode 100644 index b90fb36..0000000 --- a/concord/assemblage.lua +++ /dev/null @@ -1,49 +0,0 @@ ---- Gives an entity a set of components. --- @classmod Assemblage - -local Assemblage = {} -Assemblage.__mt = { - __index = Assemblage, -} - ---- Creates a new Assemblage. --- @tparam function assemble Function that assembles an Entity --- @treturn Assemblage A new assemblage -function Assemblage.new(assemble) - local assemblage = setmetatable({ - __assemble = assemble, - - __name = nil, - __isAssemblage = true, - }, Assemblage.__mt) - - return assemblage -end - ---- Assembles an Entity. --- @tparam Entity e Entity to assemble --- @param ... additional arguments to pass to the assemble function --- @treturn Assemblage self -function Assemblage:assemble(e, ...) - self.__assemble(e, ...) - - return self -end - ---- Returns true if the Assemblage has a name. --- @treturn boolean -function Assemblage:hasName() - return self.__name and true or false -end - ---- Returns the name of the Assemblage. --- @treturn string -function Assemblage:getName() - return self.__name -end - -return setmetatable(Assemblage, { - __call = function(_, ...) - return Assemblage.new(...) - end, -}) diff --git a/concord/assemblages.lua b/concord/assemblages.lua deleted file mode 100644 index 58731a8..0000000 --- a/concord/assemblages.lua +++ /dev/null @@ -1,49 +0,0 @@ ---- A container for registered @{Assemblage}s --- @module Assemblages - -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") - -local Assemblages = {} - ---- Registers an Assemblage. --- @string name Name to register under --- @tparam Assemblage assemblage Assemblage to register -function Assemblages.register(name, assemblage) - if (type(name) ~= "string") then - error("bad argument #1 to 'Assemblages.register' (string expected, got "..type(name)..")", 3) - end - - if (not Type.isAssemblage(assemblage)) then - error("bad argument #2 to 'Assemblages.register' (assemblage expected, got "..type(assemblage)..")", 3) - end - - if (rawget(Assemblages, name)) then - error("bad argument #2 to 'Assemblages.register' (Assemblage with name '"..name.."' was already registerd)", 3) - end - - Assemblages[name] = assemblage - assemblage.__name = name -end - ---- Returns true if the containter has an Assemblage with the specified name --- @string name Name of the Assemblage to check --- @treturn boolean -function Assemblages.has(name) - return Assemblages[name] and true or false -end - ---- Returns the Assemblage with the specified name --- @string name Name of the Assemblage to get --- @treturn Assemblage -function Assemblages.get(name) - return Assemblages[name] -end - - -return setmetatable(Assemblages, { - __index = function(_, name) - error("Attempt to index assemblage '"..tostring(name).."' that does not exist / was not registered", 2) - end -}) diff --git a/concord/component.lua b/concord/component.lua index 1b391ee..0ac83e6 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -1,6 +1,10 @@ --- A pure data container that is contained by a single entity. -- @classmod Component +local PATH = (...):gsub('%.[^%.]+$', '') + +local Components = require(PATH..".components") + local Component = {} Component.__mt = { __index = Component, @@ -9,7 +13,15 @@ Component.__mt = { --- Creates a new ComponentClass. -- @tparam function populate Function that populates a Component with values -- @treturn Component A new ComponentClass -function Component.new(populate) +function Component.new(name, populate) + if (type(name) ~= "string") then + error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) + end + + if (rawget(Components, name)) then + error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore + end + if (type(populate) ~= "function" and type(populate) ~= "nil") then error("bad argument #1 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) end @@ -17,7 +29,7 @@ function Component.new(populate) local componentClass = setmetatable({ __populate = populate, - __name = nil, + __name = name, __isComponentClass = true, }, Component.__mt) @@ -25,6 +37,8 @@ function Component.new(populate) __index = componentClass } + Components[name] = componentClass + return componentClass end diff --git a/concord/components.lua b/concord/components.lua index d7063da..e4116fd 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -7,42 +7,51 @@ local Type = require(PATH..".type") local Components = {} ---- Registers a ComponentClass. --- @string name Name to register under --- @tparam Component componentClass ComponentClass to register -function Components.register(name, componentClass) - if (type(name) ~= "string") then - error("bad argument #1 to 'Components.register' (string expected, got "..type(name)..")", 3) +local try = function (name) + if type(name) ~= "string" then + return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" end - if (not Type.isComponentClass(componentClass)) then - error("bad argument #2 to 'Components.register' (ComponentClass expected, got "..type(componentClass)..")", 3) + local value = rawget(Components, name) + if not value then + return false, "ComponentClass '"..name.."' does not exist / was not registered" end - if (rawget(Components, name)) then - error("bad argument #2 to 'Components.register' (ComponentClass with name '"..name.."' was already registerd)", 3) -- luacheck: ignore - end - - Components[name] = componentClass - componentClass.__name = name + return true, value end --- Returns true if the containter has the ComponentClass with the specified name -- @string name Name of the ComponentClass to check -- @treturn boolean function Components.has(name) - return Components[name] and true or false + return rawget(Components, name) and true or false +end + +--- Returns true and the ComponentClass if one was registered with the specified name +-- or false and an error otherwise +-- @string name Name of the ComponentClass to check +-- @treturn boolean +-- @treturn Component or error string +function Components.try(name) + return try(name) end --- Returns the ComponentClass with the specified name -- @string name Name of the ComponentClass to get -- @treturn Component function Components.get(name) - return Components[name] + local ok, value = try(name) + + if not ok then error(value, 2) end + + return value end return setmetatable(Components, { __index = function(_, name) - error("Attempt to index ComponentClass '"..tostring(name).."' that does not exist / was not registered", 2) - end + local ok, value = try(name) + + if not ok then error(value, 2) end + + return value end }) diff --git a/concord/entity.lua b/concord/entity.lua index da446e5..acafe01 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -34,18 +34,18 @@ function Entity.new(world) return e end -local function give(e, componentClass, ...) +local function give(e, name, componentClass, ...) local component = componentClass:__initialize(...) - e[componentClass] = component - e.__components[componentClass] = component + e[name] = component + e.__components[name] = component e:__dirty() end -local function remove(e, componentClass) - e[componentClass] = nil - e.__components[componentClass] = nil +local function remove(e, name, componentClass) + e[name] = nil + e.__components[name] = nil e:__dirty() end @@ -55,12 +55,14 @@ end -- @tparam Component componentClass ComponentClass to add an instance of -- @param ... additional arguments to pass to the Component's populate function -- @treturn Entity self -function Entity:give(componentClass, ...) - if not Type.isComponentClass(componentClass) then - error("bad argument #1 to 'Entity:give' (ComponentClass expected, got "..type(componentClass)..")", 2) +function Entity:give(name, ...) + local ok, componentClass = Components.try(name) + + if not ok then + error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) end - give(self, componentClass, ...) + give(self, name, componentClass, ...) return self end @@ -70,16 +72,18 @@ end -- @tparam Component componentClass ComponentClass to add an instance of -- @param ... additional arguments to pass to the Component's populate function -- @treturn Entity self -function Entity:ensure(componentClass, ...) - if not Type.isComponentClass(componentClass) then - error("bad argument #1 to 'Entity:ensure' (ComponentClass expected, got "..type(componentClass)..")", 2) +function Entity:ensure(name, ...) + local ok, componentClass = Components.try(name) + + if not ok then + error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) end - if self[componentClass] then + if self[name] then return self end - give(self, componentClass, ...) + give(self, name, componentClass, ...) return self end @@ -87,26 +91,28 @@ end --- Removes a Component from an Entity. -- @tparam Component componentClass ComponentClass of the Component to remove -- @treturn Entity self -function Entity:remove(componentClass) - if not Type.isComponentClass(componentClass) then - error("bad argument #1 to 'Entity:remove' (ComponentClass expected, got "..type(componentClass)..")") +function Entity:remove(name) + local ok, componentClass = Components.try(name) + + if not ok then + error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) end - remove(self, componentClass) + remove(self, name, componentClass) return self end --- Assembles an Entity. --- @tparam Assemblage assemblage Assemblage to assemble with --- @param ... additional arguments to pass to the Assemblage's assemble function. +-- @tparam function assemblage Function that will assemble an entity +-- @param ... additional arguments to pass to the assemblage function. -- @treturn Entity self function Entity:assemble(assemblage, ...) - if not Type.isAssemblage(assemblage) then - error("bad argument #1 to 'Entity:assemble' (Assemblage expected, got "..type(assemblage)..")") + if type(assemblage) ~= "function" then + error("bad argument #1 to 'Entity:assemble' (function expected, got "..type(assemblage)..")") end - assemblage:assemble(self, ...) + assemblage(self, ...) return self end @@ -135,23 +141,27 @@ end --- Returns true if the Entity has a Component. -- @tparam Component componentClass ComponentClass of the Component to check -- @treturn boolean -function Entity:has(componentClass) - if not Type.isComponentClass(componentClass) then - error("bad argument #1 to 'Entity:has' (ComponentClass expected, got "..type(componentClass)..")") +function Entity:has(name) + local ok, componentClass = Components.try(name) + + if not ok then + error("bad argument #1 to 'Entity:has' ("..componentClass..")", 2) end - return self[componentClass] ~= nil + return self[name] and true or false end --- Gets a Component from the Entity. -- @tparam Component componentClass ComponentClass of the Component to get -- @treturn table -function Entity:get(componentClass) - if not Type.isComponentClass(componentClass) then - error("bad argument #1 to 'Entity:get' (ComponentClass expected, got "..type(componentClass)..")") +function Entity:get(name) + local ok, componentClass = Components.try(name) + + if not ok then + error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) end - return self[componentClass] + return self[name] end --- Returns a table of all Components the Entity has. @@ -193,7 +203,7 @@ function Entity:deserialize(data) for i = 1, #data do local componentData = data[i] - if (not Components[componentData.__name]) then + if (not Components.has(componentData.__name)) then error("bad argument #1 to 'Entity:deserialize' (ComponentClass "..type(componentData.__name).." wasn't yet loaded)") -- luacheck: ignore end @@ -202,8 +212,8 @@ function Entity:deserialize(data) local component = componentClass:__new() component:deserialize(componentData) - self[componentClass] = component - self.__components[componentClass] = component + self[componentData.__name] = component + self.__components[componentData.__name] = component self:__dirty() end diff --git a/concord/init.lua b/concord/init.lua index 7ff46d3..1653c46 100644 --- a/concord/init.lua +++ b/concord/init.lua @@ -38,13 +38,8 @@ Concord.component = require(PATH..".component") Concord.components = require(PATH..".components") Concord.system = require(PATH..".system") -Concord.systems = require(PATH..".systems") Concord.world = require(PATH..".world") -Concord.worlds = require(PATH..".worlds") - -Concord.assemblage = require(PATH..".assemblage") -Concord.assemblages = require(PATH..".assemblages") local function load(pathOrFiles, namespace) if (type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table") then @@ -63,7 +58,8 @@ local function load(pathOrFiles, namespace) local name = file:sub(1, #file - 4) local path = pathOrFiles.."."..name - namespace.register(name, require(path)) + local value = require(path) + if namespace then namespace[name] = value end end elseif (type(pathOrFiles == "table")) then for _, path in ipairs(pathOrFiles) do @@ -78,37 +74,43 @@ local function load(pathOrFiles, namespace) name = path:sub((dotIndex or slashIndex) + 1) end - namespace.register(name, require(path)) + local value = require(path) + if namespace then namespace[name] = value end end end + + return namespace end --- Loads ComponentClasses and puts them in the Components container. -- Accepts a table of paths to files: {"component_1", "component_2", "etc"} -- Accepts a path to a directory with ComponentClasses: "components" function Concord.loadComponents(pathOrFiles) - load(pathOrFiles, Concord.components) + load(pathOrFiles, nil) + return Concord.components end --- Loads SystemClasses and puts them in the Systems container. -- Accepts a table of paths to files: {"system_1", "system_2", "etc"} -- Accepts a path to a directory with SystemClasses: "systems" -function Concord.loadSystems(pathOrFiles) - load(pathOrFiles, Concord.systems) +function Concord.loadSystems(pathOrFiles, world) + local systems = load(pathOrFiles, {}) + + if world then + for _, system in pairs(systems) do + world:addSystem(system) + end + end + + return systems end --- Loads Worlds and puts them in the Worlds container. -- Accepts a table of paths to files: {"world_1", "world_2", "etc"} -- Accepts a path to a directory with Worlds: "worlds" function Concord.loadWorlds(pathOrFiles) - load(pathOrFiles, Concord.worlds) + return load(pathOrFiles, {}) end ---- Loads Assemblages and puts them in the Assemblages container. --- Accepts a table of paths to files: {"assemblage_1", "assemblage_2", "etc"} --- Accepts a path to a directory with Assemblages: "assemblages" -function Concord.loadAssemblages(pathOrFiles) - load(pathOrFiles, Concord.assemblages) -end return Concord diff --git a/concord/list.lua b/concord/list.lua index 215d1bb..1263a72 100644 --- a/concord/list.lua +++ b/concord/list.lua @@ -19,7 +19,7 @@ end -- Object may not be the string 'size' -- @param obj Object to add -- @treturn List self -function List:__add(obj) +function List:add(obj) local size = self.size + 1 self[size] = obj @@ -32,7 +32,7 @@ end --- Removes an object from the List. -- @param obj Object to remove -- @treturn List self -function List:__remove(obj) +function List:remove(obj) local index = self[obj] if not index then return end local size = self.size @@ -56,7 +56,7 @@ end --- Clears the List completely. -- @treturn List self -function List:__clear() +function List:clear() for i = 1, self.size do local o = self[i] diff --git a/concord/pool.lua b/concord/pool.lua index ef22aed..e74227a 100644 --- a/concord/pool.lua +++ b/concord/pool.lua @@ -29,32 +29,53 @@ end --- Checks if an Entity is eligible for the Pool. -- @tparam Entity e Entity to check -- @treturn boolean -function Pool:__eligible(e) - for _, component in ipairs(self.__filter) do - if not e[component] then - return false - end +function Pool:eligible(e) + for i=#self.__filter, 1, -1 do + local component = self.__filter[i].__name + + if not e[component] then return false end end return true end --- Internal: Adds an Entity to the Pool. +-- Adds an Entity to the Pool, if it can be eligible. -- @param e Entity to add -- @treturn Pool self -function Pool:__add(e) - List.__add(self, e) +-- @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 --- Internal: Removed an Entity from the Pool. --- @param e Entity to remove +--- Evaluate whether an Entity should be added or removed from the Pool. +-- @param e Entity to add or remove -- @treturn Pool self -function Pool:__remove(e) - List.__remove(self, e) - self:onEntityRemoved(e) +function Pool:evaluate(e) + local has = self:has(e) + local eligible = self:eligible(e) + + if not has and eligible then + self:add(e) + elseif has and not eligible then + self:remove(e) + end return self end diff --git a/concord/system.lua b/concord/system.lua index 6ff374e..3583010 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -5,8 +5,9 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Pool = require(PATH..".pool") -local Utils = require(PATH..".utils") +local Pool = require(PATH..".pool") +local Utils = require(PATH..".utils") +local Components = require(PATH..".components") local System = { ENABLE_OPTIMIZATION = true, @@ -25,21 +26,18 @@ System.mt = { __isSystemClass = false, -- Overwrite value from systemClass }, systemClass) - -- Optimization: We deep copy the World class into our instance of a world. + -- Optimization: We deep copy the System class into our instance of a system. -- This grants slightly faster access times at the cost of memory. -- Since there (generally) won't be many instances of worlds this is a worthwhile tradeoff if (System.ENABLE_OPTIMIZATION) then Utils.shallowCopy(systemClass, system) end - for _, filter in pairs(systemClass.__filter) do - local pool = system.__buildPool(filter) - if not system[pool.__name] then - system[pool.__name] = pool - system.__pools[#system.__pools + 1] = pool - else - error("Pool with name '"..pool.name.."' already exists.") - end + for name, filter in pairs(systemClass.__filter) do + local pool = Pool(name, filter) + + system[name] = pool + system.__pools[#system.__pools + 1] = pool end system:init(world) @@ -48,12 +46,41 @@ System.mt = { end, } +local validateFilters = function (baseFilters) + local filters = {} + + for name, componentsList in pairs(baseFilters) 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 + + local filter = {} + for n, component in ipairs(componentsList) do + local ok, componentClass = Components.try(component) + + if not ok then + error("Invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3) + end + + filter[#filter + 1] = componentClass + end + + filters[name] = filter + end + + return filters +end + --- Creates a new SystemClass. --- @param ... Variable amounts of filters +-- @param table filters A table containing filters (name = {components...}) -- @treturn System A new SystemClass -function System.new(...) +function System.new(filters) local systemClass = setmetatable({ - __filter = {...}, + __filter = validateFilters(filters), __name = nil, __isSystemClass = true, @@ -70,37 +97,12 @@ function System.new(...) return systemClass end --- Internal: Builds a Pool for the System. --- @param baseFilter The 'raw' Filter --- @return A new Pool -function System.__buildPool(baseFilter) - local name = "pool" - local filter = {} - - for _, value in ipairs(baseFilter) do - if type(value) == "table" then - filter[#filter + 1] = value - elseif type(value) == "string" then - name = value - end - end - - return Pool(name, filter) -end - -- Internal: Evaluates an Entity for all the System's Pools. -- @param e The Entity to check -- @treturn System self function System:__evaluate(e) for _, pool in ipairs(self.__pools) do - local has = pool:has(e) - local eligible = pool:__eligible(e) - - if not has and eligible then - pool:__add(e) - elseif has and not eligible then - pool:__remove(e) - end + pool:evaluate(e) end return self @@ -112,7 +114,7 @@ end function System:__remove(e) for _, pool in ipairs(self.__pools) do if pool:has(e) then - pool:__remove(e) + pool:remove(e) end end @@ -123,36 +125,12 @@ end -- @treturn System self function System:__clear() for i = 1, #self.__pools do - self.__pools[i]:__clear() + self.__pools[i]:clear() end return self end ---- Enables the System. --- @treturn System self -function System:enable() - self:setEnabled(true) - - return self -end - ---- Disables the System. --- @treturn System self -function System:disable() - self:setEnabled(false) - - return self -end - ---- Toggles if the System is enabled. --- @treturn System self -function System:toggleEnabled() - self:setEnabled(not self.__enabled) - - return self -end - --- Sets if the System is enabled -- @tparam boolean enable -- @treturn System self diff --git a/concord/systems.lua b/concord/systems.lua deleted file mode 100644 index f29550d..0000000 --- a/concord/systems.lua +++ /dev/null @@ -1,48 +0,0 @@ ---- Container for registered SystemClasses --- @module Systems - -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") - -local Systems = {} - ---- Registers a SystemClass. --- @tparam string name Name to register under --- @tparam System systemClass SystemClass to register -function Systems.register(name, systemClass) - if (type(name) ~= "string") then - error("bad argument #1 to 'Systems.register' (string expected, got "..type(name)..")", 3) - end - - if (not Type.isSystemClass(systemClass)) then - error("bad argument #2 to 'Systems.register' (systemClass expected, got "..type(systemClass)..")", 3) - end - - if (rawget(Systems, name)) then - error("bad argument #2 to 'Systems.register' (System with name '"..name.."' is already registerd)", 3) - end - - Systems[name] = systemClass - systemClass.__name = name -end - ---- Returns true if the containter has the SystemClass with the name --- @tparam string name Name of the SystemClass to check --- @treturn boolean -function Systems.has(name) - return Systems[name] and true or false -end - ---- Returns the SystemClass with the name --- @tparam string name Name of the SystemClass to get --- @return SystemClass with the name -function Systems.get(name) - return Systems[name] -end - -return setmetatable(Systems, { - __index = function(_, name) - error("Attempt to index system '"..tostring(name).."' that does not exist / was not registered", 2) - end -}) diff --git a/concord/type.lua b/concord/type.lua index 62ef0f9..157a8d4 100644 --- a/concord/type.lua +++ b/concord/type.lua @@ -45,11 +45,4 @@ function Type.isWorld(t) return type(t) == "table" and t.__isWorld or false end ---- Returns if object is an Assemblage. --- @param t Object to check --- @treturn boolean -function Type.isAssemblage(t) - return type(t) == "table" and t.__isAssemblage or false -end - return Type diff --git a/concord/world.lua b/concord/world.lua index 70551c0..f42a608 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -61,7 +61,7 @@ function World:addEntity(e) end e.__world = self - self.__added:__add(e) + self.__added:add(e) return self end @@ -74,7 +74,7 @@ function World:removeEntity(e) error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) end - self.__removed:__add(e) + self.__removed:add(e) return self end @@ -83,7 +83,7 @@ end -- @param e Entity to mark as dirty function World:__dirtyEntity(e) if not self.__dirty:has(e) then - self.__dirty:__add(e) + self.__dirty:add(e) end end @@ -107,40 +107,46 @@ function World:__flush() for i = 1, self.__backAdded.size do e = self.__backAdded[i] - self.__entities:__add(e) + if e.__world == self then + self.__entities:add(e) - for j = 1, self.__systems.size do - self.__systems[j]:__evaluate(e) + for j = 1, self.__systems.size do + self.__systems[j]:__evaluate(e) + end + + self:onEntityAdded(e) end - - self:onEntityAdded(e) end - self.__backAdded:__clear() + self.__backAdded:clear() -- Process removed entities for i = 1, self.__backRemoved.size do e = self.__backRemoved[i] - e.__world = nil - self.__entities:__remove(e) + if e.__world == self then + e.__world = nil + self.__entities:remove(e) - for j = 1, self.__systems.size do - self.__systems[j]:__remove(e) + for j = 1, self.__systems.size do + self.__systems[j]:__remove(e) + end + + self:onEntityRemoved(e) end - - self:onEntityRemoved(e) end - self.__backRemoved:__clear() + self.__backRemoved:clear() -- Process dirty entities for i = 1, self.__backDirty.size do e = self.__backDirty[i] - for j = 1, self.__systems.size do - self.__systems[j]:__evaluate(e) + if e.__world == self then + for j = 1, self.__systems.size do + self.__systems[j]:__evaluate(e) + end end end - self.__backDirty:__clear() + self.__backDirty:clear() return self end @@ -171,7 +177,7 @@ function World:addSystem(systemClass) local system = systemClass(self) self.__systemLookup[systemClass] = system - self.__systems:__add(system) + self.__systems:add(system) for callbackName, callback in pairs(systemClass) do -- Skip callback if its blacklisted @@ -278,9 +284,13 @@ function World:clear() self:removeEntity(self.__entities[i]) end - for i = 1, self.__systems.size do - self.__systems[i]:__clear() + for i = 1, self.__added.size do + local e = self.__added[i] + e.__world = nil end + self.__added:clear() + + self:__flush() return self end diff --git a/concord/worlds.lua b/concord/worlds.lua deleted file mode 100644 index 6feba44..0000000 --- a/concord/worlds.lua +++ /dev/null @@ -1,48 +0,0 @@ ---- Worlds --- Container for registered Worlds - -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") - -local Worlds = {} - ---- Registers a World. --- @tparam string name Name to register under --- @param world World to register -function Worlds.register(name, world) - if (type(name) ~= "string") then - error("bad argument #1 to 'Worlds.register' (string expected, got "..type(name)..")", 3) - end - - if (not Type.isWorld(world)) then - error("bad argument #2 to 'Worlds.register' (world expected, got "..type(world)..")", 3) - end - - if (rawget(Worlds, name)) then - error("bad argument #2 to 'Worlds.register' (World with name '"..name.."' is already registerd)", 3) - end - - Worlds[name] = world - world.__name = name -end - ---- Returns true if the containter has the World with the name --- @tparam string name Name of the World to check --- @treturn boolean -function Worlds.has(name) - return Worlds[name] and true or false -end - ---- Returns the World with the name --- @tparam string name Name of the World to get --- @return World with the name -function Worlds.get(name) - return Worlds[name] -end - -return setmetatable(Worlds, { - __index = function(_, name) - error("Attempt to index world '"..tostring(name).."' that does not exist / was not registered", 2) - end -}) diff --git a/examples/assemblageTest/init.lua b/examples/assemblageTest/init.lua index 01adb8b..4136936 100644 --- a/examples/assemblageTest/init.lua +++ b/examples/assemblageTest/init.lua @@ -5,61 +5,59 @@ local Component = Concord.component local System = Concord.system local Assemblage = Concord.assemblage -local Game = Concord.context() - -local Legs = Component(function(e, legCount) +local Legs = Component("Legs", function(e, legCount) e.legCount = legCount or 0 end) -local Instinct = Component(function(e) -- luacheck: ignore +local Instinct = Component("Instinct", function(e) -- luacheck: ignore end) -local Cool = Component(function(e, coolness) +local Cool = Component("Cool", function(e, coolness) e.coolness = coolness end) -local Wings = Component(function(e) +local Wings = Component("Wings", function(e) e.wingCount = 2 end) -local Animal = Assemblage(function(e, legCount) +local Animal = function(e, legCount) e - :give(Legs, legCount) - :give(Instinct) + :give("Legs", legCount) + :give("Instinct") print("Animal") -end) +end -local Lion = Assemblage(function(e, coolness) +local Lion = function(e, coolness) e :assemble(Animal, 4) :give(Cool, coolness) print("Lion") -end) +end -local Eagle = Assemblage(function(e) +local Eagle = function(e) e :assemble(Animal, 2) :give(Wings) print("Eagle") -end) +end -local Griffin = Assemblage(function(e, coolness) +local Griffin = function(e, coolness) e :assemble(Animal, 4) :assemble(Lion, coolness * 2) :assemble(Eagle) -end) +end local myAnimal = Entity() :assemble(Griffin, 5) --:apply() -print(myAnimal:has(Legs)) -print(myAnimal:has(Instinct)) -print(myAnimal:has(Cool)) -print(myAnimal:has(Wings)) +print(myAnimal:has("Legs")) +print(myAnimal:has("Instinct")) +print(myAnimal:has("Cool")) +print(myAnimal:has("Wings")) diff --git a/examples/serialization/init.lua b/examples/serialization/init.lua index 2b5e05d..c362265 100644 --- a/examples/serialization/init.lua +++ b/examples/serialization/init.lua @@ -11,11 +11,10 @@ local function display(t) end end -local test_component_1 = Concord.component(function(e, x, y) +local test_component_1 = Concord.component("test_component_1", function(e, x, y) e.x = x or 0 e.y = y or 0 end) -Concord.components.register("test_component_1", test_component_1) function test_component_1:serialize() return { @@ -29,10 +28,9 @@ function test_component_1:deserialize(data) self.y = data.y or 0 end -local test_component_2 = Concord.component(function(e, foo) +local test_component_2 = Concord.component("test_component_2", function(e, foo) e.foo = foo end) -Concord.components.register("test_component_2", test_component_2) function test_component_2:serialize() return { @@ -50,8 +48,8 @@ local world_2 = Concord.world() -- Test Entity Concord.entity(world_1) -:give(test_component_1, 100, 50) -:give(test_component_2, "Hello World!") +:give("test_component_1", 100, 50) +:give("test_component_2", "Hello World!") -- Serialize world local data = world_1:serialize() @@ -62,8 +60,8 @@ world_2:deserialize(data) -- Check result local test_entity_copy = world_2:getEntities()[1] -local test_comp_1 = test_entity_copy[test_component_1] -local test_comp_2 = test_entity_copy[test_component_2] +local test_comp_1 = test_entity_copy["test_component_1"] +local test_comp_2 = test_entity_copy["test_component_2"] print(test_comp_1.x, test_comp_1.y) print(test_comp_2.foo) \ No newline at end of file diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index 29b31d1..6afd986 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -6,33 +6,33 @@ local System = Concord.system local Game = Concord.world() -local Position = Component(function(e, x, y) +local Position = Component('Position', function(e, x, y) e.x = x e.y = y end) -local Rectangle = Component(function(e, w, h) +local Rectangle = Component('Rectangle', function(e, w, h) e.w = w e.h = h end) -local Circle = Component(function(e, r) +local Circle = Component('Circle', function(e, r) e.r = r end) -local Color = Component(function(e, r, g, b, a) +local Color = Component('Color', function(e, r, g, b, a) e.r = r e.g = g e.b = b e.a = a end) -local RectangleRenderer = System({Position, Rectangle}) +local RectangleRenderer = System{pool = {'Position', 'Rectangle'}} function RectangleRenderer:draw() for _, e in ipairs(self.pool) do - local position = e:get(Position) - local rectangle = e:get(Rectangle) - local color = e:get(Color) + local position = e:get('Position') + local rectangle = e:get('Rectangle') + local color = e:get('Color') love.graphics.setColor(255, 255, 255) if color then @@ -43,7 +43,7 @@ function RectangleRenderer:draw() end end -local CircleRenderer = System({Position, Circle}) +local CircleRenderer = System{pool = {'Position', 'Circle'}} function CircleRenderer:flush() for _, e in ipairs(self.pool.removed) do print(tostring(e).. " was removed from my pool D:") @@ -52,9 +52,9 @@ end function CircleRenderer:draw() for _, e in ipairs(self.pool) do - local position = e:get(Position) - local circle = e:get(Circle) - local color = e:get(Color) + local position = e:get('Position') + local circle = e:get('Circle') + local color = e:get('Color') love.graphics.setColor(255, 255, 255) if color then @@ -65,7 +65,7 @@ function CircleRenderer:draw() end end -local RandomRemover = System({}) +local RandomRemover = System{pool = {}} function RandomRemover:init() self.time = 0 @@ -93,11 +93,11 @@ Game:addSystem(CircleRenderer(), "draw") for _ = 1, 100 do local e = Entity() - e:give(Position, love.math.random(0, 700), love.math.random(0, 700)) - e:give(Rectangle, love.math.random(5, 20), love.math.random(5, 20)) + e:give('Position', love.math.random(0, 700), love.math.random(0, 700)) + e:give('Rectangle', love.math.random(5, 20), love.math.random(5, 20)) if love.math.random(0, 1) == 0 then - e:give(Color, love.math.random(), love.math.random(), love.math.random(), 1) + e:give('Color', love.math.random(), love.math.random(), love.math.random(), 1) end Game:addEntity(e) @@ -105,11 +105,11 @@ end for _ = 1, 100 do local e = Entity() - e:give(Position, love.math.random(0, 700), love.math.random(0, 700)) - e:give(Circle, love.math.random(5, 20)) + e:give('Position', love.math.random(0, 700), love.math.random(0, 700)) + e:give('Circle', love.math.random(5, 20)) if love.math.random(0, 1) == 0 then - e:give(Color, love.math.random(), love.math.random(), love.math.random(), 1) + e:give('Color', love.math.random(), love.math.random(), love.math.random(), 1) end Game:addEntity(e)