From 11255fd72268036c05b61ba6759ba4f42cebd32b Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Oct 2018 21:52:23 +0200 Subject: [PATCH 001/113] Optimized Lists. Added pool.added and .removed. Added entity:ensure --- TODO | 8 ++++++ examples/simpleDrawing/init.lua | 22 ++++++++-------- lib/entity.lua | 28 +++++++++++++++++--- lib/instance.lua | 4 +++ lib/list.lua | 45 ++++++++++++++++++--------------- lib/pool.lua | 9 +++++++ lib/system.lua | 36 ++++++++++++++++---------- 7 files changed, 104 insertions(+), 48 deletions(-) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..4ef844e --- /dev/null +++ b/TODO @@ -0,0 +1,8 @@ +[x] Modify pools and list to be more efficient +[x] Rework Entity:give to remove any previous component and re-add it. +[x] Add Entity:ensure (maybe?) +[ ] Put pools in the Instance and invert dependency. +[ ] Share pools between systems +[ ] Remove System callbacks +[x] Put .added and .removed in pools so they can be iterated over +[ ] Implement assemblages diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index 9879f94..f9a2d9f 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -31,10 +31,7 @@ end) local RectangleRenderer = System({Position, Rectangle}) function RectangleRenderer:draw() - local e - for i = 1, self.pool.size do - e = self.pool:get(i) - + for _, e in ipairs(self.pool) do local position = e:get(Position) local rectangle = e:get(Rectangle) local color = e:get(Color) @@ -49,11 +46,16 @@ function RectangleRenderer:draw() end local CircleRenderer = System({Position, Circle}) -function CircleRenderer:draw() - local e - for i = 1, self.pool.size do - e = self.pool:get(i) +function CircleRenderer:flush() + for _, e in ipairs(self.pool.removed) do + print(tostring(e).. " was removed from my pool D:") + end + self:clear() +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) @@ -76,7 +78,7 @@ end function RandomRemover:update(dt) self.time = self.time + dt - if self.time >= 0.25 then + if self.time >= 0.5 then self.time = 0 if self.pool.size > 0 then @@ -115,4 +117,4 @@ for i = 1, 100 do end Game:addEntity(e) -end \ No newline at end of file +end diff --git a/lib/entity.lua b/lib/entity.lua index 68fedeb..bfb9a07 100644 --- a/lib/entity.lua +++ b/lib/entity.lua @@ -22,6 +22,12 @@ function Entity.new() return e end +local function give(e, component, ...) + local comp = component:__initialize(...) + e.components[component] = comp + e[component] = comp +end + --- Gives an Entity a component with values. -- @param component The Component to add -- @param ... The values passed to the Component @@ -31,9 +37,25 @@ function Entity:give(component, ...) error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2) end - local comp = component:__initialize(...) - self.components[component] = comp - self[component] = comp + if self[component] then + self:remove(component):apply() + end + + give(self, component, ...) + + return self +end + +function Entity:ensure(component, ...) + if not Type.isComponent(component) then + error("bad argument #1 to 'Entity:ensure' (Component expected, got "..type(component)..")", 2) + end + + if self[component] then + return self + end + + give(self, component, ...) return self end diff --git a/lib/instance.lua b/lib/instance.lua index f2a947e..f4d63a0 100644 --- a/lib/instance.lua +++ b/lib/instance.lua @@ -92,6 +92,10 @@ function Instance:flush() end end + for i = 1, self.systems.size do + self.systems:get(i):flush() + end + return self end diff --git a/lib/list.lua b/lib/list.lua index 33b2c10..a5698c9 100644 --- a/lib/list.lua +++ b/lib/list.lua @@ -7,18 +7,21 @@ local mt = {__index = List} -- @return A new list function List.new() return setmetatable({ - objects = {}, - pointers = {}, - size = 0, + size = 0, }, mt) end --- Clears the List completely. -- @return self function List:clear() - self.objects = {} - self.pointers = {} - self.size = 0 + for i = 1, self.size do + local o = self[i] + + self[o] = nil + self[i] = nil + end + + self.size = 0 return self end @@ -26,12 +29,12 @@ end --- Adds an object to the List. -- @param obj The object to add -- @return self -function List:add(obj) +function List:add(obj) -- obj can not be a number and also not the string "size" local size = self.size + 1 - self.objects[size] = obj - self.pointers[obj] = size - self.size = size + self[size] = obj + self[obj] = size + self.size = size return self end @@ -40,22 +43,22 @@ end -- @param obj The object to remove -- @return self function List:remove(obj) - local index = self.pointers[obj] - if not index then return end + local index = self[obj] + if not index then return end local size = self.size - + if index == size then - self.objects[size] = nil + self[size] = nil else - local other = self.objects[size] + local other = self[size] - self.objects[index] = other - self.pointers[other] = index + self[index] = other + self[other] = index - self.objects[size] = nil + self[size] = nil end - self.pointers[obj] = nil + self[obj] = nil self.size = size - 1 end @@ -63,14 +66,14 @@ end -- @param index The index to look at -- @return The object at the index function List:get(index) - return self.objects[index] + return self[index] end --- Gets if the List has the object. -- @param obj The object to search for -- true if the list has the object, false otherwise function List:has(obj) - return self.pointers[obj] and true + return self[obj] and true end return setmetatable(List, { diff --git a/lib/pool.lua b/lib/pool.lua index c2d1ab4..5a7f44b 100644 --- a/lib/pool.lua +++ b/lib/pool.lua @@ -14,6 +14,9 @@ Pool.__index = Pool function Pool.new(name, filter) local pool = setmetatable(List(), Pool) + pool.added = {} + pool.removed = {} + pool.name = name pool.filter = filter @@ -22,6 +25,12 @@ function Pool.new(name, filter) return pool end +function Pool:flush() + for i = 1, math.max(#self.added, #self.removed) do + self.added[i], self.removed[i] = nil, nil + end +end + --- Checks if an Entity is eligible for the Pool. -- @param e The Entity to check -- @return True if the entity is eligible, false otherwise diff --git a/lib/system.lua b/lib/system.lua index ac769ba..7c7b22a 100644 --- a/lib/system.lua +++ b/lib/system.lua @@ -66,7 +66,7 @@ end -- @param e The Entity to check -- @return True if the Entity was added, false if it was removed. Nil if nothing happend function System:__check(e) - local systemHas = self:__has(e) + local systemHas = self.__all[e] for _, pool in ipairs(self.__pools) do local poolHas = pool:has(e) @@ -74,23 +74,28 @@ function System:__check(e) if not poolHas and eligible then pool:add(e) + pool.added[#pool.added + 1] = e + self:entityAddedTo(e, pool) self:__tryAdd(e) elseif poolHas and not eligible then pool:remove(e) + pool.removed[#pool.removed + 1] = e + self:entityRemovedFrom(e, pool) self:__tryRemove(e) end end end ---- Removed an Entity from the System. +--- Remove an Entity from the System. -- @param e The Entity to remove function System:__remove(e) - if self:__has(e) then + if self.__all[e] then for _, pool in ipairs(self.__pools) do if pool:has(e) then pool:remove(e) + pool.removed[#pool.removed + 1] = e self:entityRemovedFrom(e, pool) end end @@ -103,7 +108,7 @@ end --- Tries to add an Entity to the System. -- @param e The Entity to add function System:__tryAdd(e) - if not self:__has(e) then + if not self.__all[e] then self.__all[e] = 0 self:entityAdded(e) end @@ -114,7 +119,7 @@ end --- Tries to remove an Entity from the System. -- @param e The Entity to remove function System:__tryRemove(e) - if self:__has(e) then + if self.__all[e] then self.__all[e] = self.__all[e] - 1 if self.__all[e] == 0 then @@ -124,19 +129,22 @@ function System:__tryRemove(e) end end +function System:flush() + self:clear() +end + +function System:clear() + for i = 1, #self.__pools do + self.__pools[i]:flush() + end +end + --- Returns the Instance the System is in. -- @return The Instance function System:getInstance() return self.__instance end ---- Returns if the System has the Entity. --- @param e The Entity to check for --- @return True if the System has the Entity. False otherwise -function System:__has(e) - return self.__all[e] and true -end - --- Default callback for system initialization. -- @param ... Varags function System:init(...) @@ -170,12 +178,12 @@ function System:addedTo(instance) end -- Default callback for when a System's callback is enabled. --- @param callbackName The name of the callback that was enabled +-- @param callbackName The name of the callback that was enabled function System:enabledCallback(callbackName) end -- Default callback for when a System's callback is disabled. --- @param callbackName The name of the callback that was disabled +-- @param callbackName The name of the callback that was disabled function System:disabledCallback(callbackName) end From f7a394f057400ee156f17afa402a3e7318e5c4c6 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Sun, 11 Nov 2018 19:42:44 +0100 Subject: [PATCH 002/113] Added assemblages --- .luacheckrc | 1 + TODO | 11 ++++- examples/assemblageTest/init.lua | 67 +++++++++++++++++++++++++++++ examples/simpleDrawing/init.lua | 6 +-- lib/assemblage.lua | 26 ++++++++++++ lib/entity.lua | 44 ++++++++++++------- lib/init.lua | 11 ++--- lib/instance.lua | 73 +++++++++++++++++++++----------- lib/pool.lua | 2 +- lib/system.lua | 48 ++++----------------- lib/type.lua | 6 ++- main.lua | 2 +- 12 files changed, 204 insertions(+), 93 deletions(-) create mode 100644 .luacheckrc create mode 100644 examples/assemblageTest/init.lua create mode 100644 lib/assemblage.lua diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..b557738 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1 @@ +std="love+luajit" diff --git a/TODO b/TODO index 4ef844e..33fc816 100644 --- a/TODO +++ b/TODO @@ -3,6 +3,13 @@ [x] Add Entity:ensure (maybe?) [ ] Put pools in the Instance and invert dependency. [ ] Share pools between systems -[ ] Remove System callbacks +[x] Remove System callbacks [x] Put .added and .removed in pools so they can be iterated over -[ ] Implement assemblages +[x] Implement assemblages +[x] Do :apply automatically with marked entities +[x] Remove Entity.components. Use Entity[Component] instead + +[ ] Add missing documentation +[ ] Fix current documentation +[ ] Write unit tests +[ ] Write examples diff --git a/examples/assemblageTest/init.lua b/examples/assemblageTest/init.lua new file mode 100644 index 0000000..730129b --- /dev/null +++ b/examples/assemblageTest/init.lua @@ -0,0 +1,67 @@ +local Concord = require("lib").init({ + useEvents = true +}) +local Entity = Concord.entity +local Component = Concord.component +local System = Concord.system +local Assemblage = Concord.assemblage + +local Game = Concord.instance() +Concord.addInstance(Game) + +local Legs = Component(function(e, legCount) + e.legCount = legCount or 0 +end) + +local Instinct = Component(function(e) -- luacheck: ignore +end) + +local Cool = Component(function(e, coolness) + e.coolness = coolness +end) + +local Wings = Component(function(e) + e.wingCount = 2 +end) + + +local Animal = Assemblage(function(e, legCount) + e + :give(Legs, legCount) + :give(Instinct) + + print("Animal") +end) + +local Lion = Assemblage(function(e, coolness) + e + :assemble(Animal, 4) + :give(Cool, coolness) + + print("Lion") +end) + +local Eagle = Assemblage(function(e) + e + :assemble(Animal, 2) + :give(Wings) + + print("Eagle") +end) + +local Griffin = Assemblage(function(e, coolness) + e + :assemble(Animal, 4) + :assemble(Lion, coolness * 2) + :assemble(Eagle) +end) + + +local myAnimal = Entity() +:assemble(Griffin, 5) +--:apply() + +print(myAnimal:has(Legs)) +print(myAnimal:has(Instinct)) +print(myAnimal:has(Cool)) +print(myAnimal:has(Wings)) diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index f9a2d9f..8cc0426 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -78,7 +78,7 @@ end function RandomRemover:update(dt) self.time = self.time + dt - if self.time >= 0.5 then + if self.time >= 0.05 then self.time = 0 if self.pool.size > 0 then @@ -95,7 +95,7 @@ Game:addSystem(RandomRemover(), "update") Game:addSystem(RectangleRenderer(), "draw") Game:addSystem(CircleRenderer(), "draw") -for i = 1, 100 do +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)) @@ -107,7 +107,7 @@ for i = 1, 100 do Game:addEntity(e) end -for i = 1, 100 do +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)) diff --git a/lib/assemblage.lua b/lib/assemblage.lua new file mode 100644 index 0000000..67a1428 --- /dev/null +++ b/lib/assemblage.lua @@ -0,0 +1,26 @@ +--- Assemblage + +local Assemblage = {} +Assemblage.__index = Assemblage + +function Assemblage.new(assemble) + local assemblage = setmetatable({ + __assemble = assemble, + + __isAssemblage = true, + }, Assemblage) + + Assemblage.__mt = {__index = assemblage} + + return assemblage +end + +function Assemblage:assemble(e, ...) + self.__assemble(e, ...) + + return self +end + +return setmetatable(Assemblage, { + __call = function(_, ...) return Assemblage.new(...) end, +}) diff --git a/lib/entity.lua b/lib/entity.lua index bfb9a07..441b9df 100644 --- a/lib/entity.lua +++ b/lib/entity.lua @@ -12,9 +12,8 @@ Entity.__index = Entity -- @return A new Entity function Entity.new() local e = setmetatable({ - components = {}, - removed = {}, - instances = List(), + removed = {}, + instances = List(), __isEntity = true, }, Entity) @@ -24,8 +23,9 @@ end local function give(e, component, ...) local comp = component:__initialize(...) - e.components[component] = comp e[component] = comp + + e:mark() end --- Gives an Entity a component with values. @@ -68,22 +68,36 @@ function Entity:remove(component) error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")") end - self.removed[component] = true + self.removed[#self.removed + 1] = component + + self:mark() return self end ---- Checks the Entity against the pools again. --- @return self -function Entity:apply() - for i = 1, self.instances.size do - self.instances:get(i):checkEntity(self) +function Entity:assemble(assemblage, ...) + if not Type.isAssemblage(assemblage) then + error("bad argument #1 to 'Entity:assemble' (Assemblage expected, got "..type(assemblage)..")") end - for component, _ in pairs(self.removed) do - self.components[component] = nil + assemblage:assemble(self, ...) + + return self +end + +function Entity:mark() + for i = 1, self.instances.size do + self.instances:get(i):markEntity(self) + end +end + +function Entity:apply() + for i = 1, #self.removed do + local component = self.removed[i] + self[component] = nil - self.removed[component] = nil + + self.removed[i] = nil end return self @@ -107,7 +121,7 @@ function Entity:get(component) error("bad argument #1 to 'Entity:get' (Component expected, got "..type(component)..")") end - return self.components[component] + return self[component] end --- Returns true if the Entity has the Component. @@ -118,7 +132,7 @@ function Entity:has(component) error("bad argument #1 to 'Entity:has' (Component expected, got "..type(component)..")") end - return self.components[component] ~= nil + return self[component] ~= nil end return setmetatable(Entity, { diff --git a/lib/init.lua b/lib/init.lua index 76f2588..81887fb 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -22,7 +22,7 @@ local Concord = { The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. @@ -39,10 +39,11 @@ local Concord = { -- } -- @return Concord function Concord.init(settings) - Concord.entity = require(PATH..".entity") - Concord.component = require(PATH..".component") - Concord.system = require(PATH..".system") - Concord.instance = require(PATH..".instance") + Concord.entity = require(PATH..".entity") + Concord.component = require(PATH..".component") + Concord.system = require(PATH..".system") + Concord.instance = require(PATH..".instance") + Concord.assemblage = require(PATH..".assemblage") if settings and settings.useEvents then Concord.instances = {} diff --git a/lib/instance.lua b/lib/instance.lua index f4d63a0..fa59c35 100644 --- a/lib/instance.lua +++ b/lib/instance.lua @@ -2,8 +2,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Entity = require(PATH..".entity") -local System = require(PATH..".system") local Type = require(PATH..".type") local List = require(PATH..".list") @@ -17,8 +15,9 @@ function Instance.new() entities = List(), systems = List(), events = {}, + + marked = {}, removed = {}, - toRemove = nil, __isInstance = true, }, Instance) @@ -43,6 +42,29 @@ function Instance:addEntity(e) return self end +--- Marks an Entity as removed from the Instance. +-- @param e The Entity to mark +-- @return self +function Instance:removeEntity(e) + if not Type.isEntity(e) then + error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2) + end + + self.removed[#self.removed + 1] = e + + return self +end + +function Instance:markEntity(e) + if not Type.isEntity(e) then + error("bad argument #1 to 'Instance:markEntity' (Entity expected, got "..type(e)..")", 2) + end + + self.marked[#self.marked + 1] = e + + return self +end + --- Checks an Entity against all the systems in the Instance. -- @param e The Entity to check -- @return self @@ -58,34 +80,33 @@ function Instance:checkEntity(e) return self end ---- Marks an Entity as removed from the Instance. --- @param e The Entity to mark --- @return self -function Instance:removeEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2) - end - - self.removed[#self.removed + 1] = e - - return self -end - --- Completely removes all marked Entities in the Instance. -- @return self function Instance:flush() - while #self.removed > 0 do - self.toRemove = self.removed + while #self.marked > 0 do + local marked = self.removed self.removed = {} - for i = 1, #self.toRemove do - local e = self.toRemove[i] + for i = 1, #marked do + local e = marked[i] + + e.instances:apply() + e.instances:checkEntity(e) + end + end + + while #self.removed > 0 do + local removed = self.removed + self.removed = {} + + for i = 1, #removed do + local e = removed[i] e.instances:remove(self) self.entities:remove(e) - for i = 1, self.systems.size do - self.systems:get(i):__remove(e) + for j = 1, self.systems.size do + self.systems:get(j):__remove(e) end self:onEntityRemoved(e) @@ -93,7 +114,9 @@ function Instance:flush() end for i = 1, self.systems.size do - self.systems:get(i):flush() + local system = self.systems:get(i) + system:flush() + system:clear() end return self @@ -250,12 +273,12 @@ end --- Default callback for adding an Entity. -- @param e The Entity that was added -function Instance:onEntityAdded(e) +function Instance:onEntityAdded(e) -- luacheck: ignore end --- Default callback for removing an Entity. -- @param e The Entity that was removed -function Instance:onEntityRemoved(e) +function Instance:onEntityRemoved(e) -- luacheck: ignore end return setmetatable(Instance, { diff --git a/lib/pool.lua b/lib/pool.lua index 5a7f44b..e7e949c 100644 --- a/lib/pool.lua +++ b/lib/pool.lua @@ -36,7 +36,7 @@ end -- @return True if the entity is eligible, false otherwise function Pool:eligible(e) for _, component in ipairs(self.filter) do - if not e.components[component] or e.removed[component] then + if not e[component] or e.removed[component] then return false end end diff --git a/lib/system.lua b/lib/system.lua index 7c7b22a..a42f79b 100644 --- a/lib/system.lua +++ b/lib/system.lua @@ -2,8 +2,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Component = require(PATH..".component") -local Pool = require(PATH..".pool") +local Pool = require(PATH..".pool") local System = {} System.mt = { @@ -47,11 +46,11 @@ end --- Builds a Pool for the System. -- @param baseFilter The 'raw' Filter -- @return A new Pool -function System:__buildPool(baseFilter) +function System:__buildPool(baseFilter) -- luacheck: ignore local name = "pool" local filter = {} - for i, v in ipairs(baseFilter) do + for _, v in ipairs(baseFilter) do if type(v) == "table" then filter[#filter + 1] = v elseif type(v) == "string" then @@ -66,8 +65,6 @@ end -- @param e The Entity to check -- @return True if the Entity was added, false if it was removed. Nil if nothing happend function System:__check(e) - local systemHas = self.__all[e] - for _, pool in ipairs(self.__pools) do local poolHas = pool:has(e) local eligible = pool:eligible(e) @@ -76,13 +73,11 @@ function System:__check(e) pool:add(e) pool.added[#pool.added + 1] = e - self:entityAddedTo(e, pool) self:__tryAdd(e) elseif poolHas and not eligible then pool:remove(e) pool.removed[#pool.removed + 1] = e - self:entityRemovedFrom(e, pool) self:__tryRemove(e) end end @@ -96,12 +91,10 @@ function System:__remove(e) if pool:has(e) then pool:remove(e) pool.removed[#pool.removed + 1] = e - self:entityRemovedFrom(e, pool) end end self.__all[e] = nil - self:entityRemoved(e) end end @@ -110,7 +103,6 @@ end function System:__tryAdd(e) if not self.__all[e] then self.__all[e] = 0 - self:entityAdded(e) end self.__all[e] = self.__all[e] + 1 @@ -124,13 +116,11 @@ function System:__tryRemove(e) if self.__all[e] == 0 then self.__all[e] = nil - self:entityRemoved(e) end end end -function System:flush() - self:clear() +function System:flush() -- luacheck: ignore end function System:clear() @@ -147,44 +137,22 @@ end --- Default callback for system initialization. -- @param ... Varags -function System:init(...) -end - ---- Default callback for adding an Entity. --- @param e The Entity that was added -function System:entityAdded(e) -end - ---- Default callback for adding an Entity to a pool. --- @param e The Entity that was added --- @param pool The pool the Entity was added to -function System:entityAddedTo(e, pool) -end - ---- Default callback for removing an Entity. --- @param e The Entity that was removed -function System:entityRemoved(e) -end - ---- Default callback for removing an Entity from a pool. --- @param e The Entity that was removed --- @param pool The pool the Entity was removed from -function System:entityRemovedFrom(e, pool) +function System:init(...) -- luacheck: ignore end -- Default callback for when the System is added to an Instance. -- @param instance The Instance the System was added to -function System:addedTo(instance) +function System:addedTo(instance) -- luacheck: ignore end -- Default callback for when a System's callback is enabled. -- @param callbackName The name of the callback that was enabled -function System:enabledCallback(callbackName) +function System:enabledCallback(callbackName) -- luacheck: ignore end -- Default callback for when a System's callback is disabled. -- @param callbackName The name of the callback that was disabled -function System:disabledCallback(callbackName) +function System:disabledCallback(callbackName) -- luacheck: ignore end return setmetatable(System, { diff --git a/lib/type.lua b/lib/type.lua index 204aa79..80d14b0 100644 --- a/lib/type.lua +++ b/lib/type.lua @@ -16,4 +16,8 @@ function Type.isInstance(t) return type(t) == "table" and t.__isInstance end -return Type \ No newline at end of file +function Type.isAssemblage(t) + return type(t) == "table" and t.__isAssemblage +end + +return Type diff --git a/main.lua b/main.lua index c809a1f..ce78707 100644 --- a/main.lua +++ b/main.lua @@ -1,4 +1,4 @@ local file = "examples.simpleDrawing" -- local file = "examples.baseLayout.main" -require(file) \ No newline at end of file +require(file) From 6ddb28ffbce89f8afe9639fa9d8a3cf03818aa53 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 26 Nov 2018 12:37:28 +0100 Subject: [PATCH 003/113] Small fixes --- LICENSE | 40 ++++++++++++------------ README.md | 78 +++++++++++++++++++++++----------------------- lib/assemblage.lua | 6 ++-- lib/component.lua | 4 ++- lib/entity.lua | 4 ++- lib/init.lua | 8 +++-- lib/instance.lua | 4 ++- lib/list.lua | 4 ++- lib/pool.lua | 4 ++- lib/system.lua | 4 ++- 10 files changed, 85 insertions(+), 71 deletions(-) diff --git a/LICENSE b/LICENSE index e3ce12d..8771a88 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2018 Justin van der Leij - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +MIT License + +Copyright (c) 2018 Justin van der Leij + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index d2187ef..32f0383 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,39 @@ -# Concord - -Concord is a feature complete ECS. -It's main focus is on speed and usage. You should be able to quickly write code that performs well. - -Documentation for Concord can be found in the [Wiki tab](https://github.com/Tjakka5/Concord/wiki). - -Auto generated docs for Concord can be found in the [Github page](https://tjakka5.github.io/Concord/). These are still work in progress and might be incomplete though. - -## Installation -Download the repository and drop it in your project, then simply require it as: -```lua -local Concord = require(PathToConcord).init() - -You will only need to call .init once when you first require it. -``` - -## Modules -Below is a list of modules. -More information about what each done can be found in the Wiki - -```lua -local Concord = require("concord") -local Entity = require("concord.entity") -local Component = require("concord.component") -local System = require("concord.system") -local Instance = require("concord.instance") -``` - -## Contributors -``` -Positive07: Constant support and a good rubberduck -Brbl: Early testing and issue reporting -Josh: Squashed a few bugs and docs -Erasio: Took inspiration from HooECS. Also introduced me to ECS. -``` - -## Licence -MIT Licensed - Copyright Justin van der Leij (Tjakka5) +# Concord + +Concord is a feature complete ECS. +It's main focus is on speed and usage. You should be able to quickly write code that performs well. + +Documentation for Concord can be found in the [Wiki tab](https://github.com/Tjakka5/Concord/wiki). + +Auto generated docs for Concord can be found in the [Github page](https://tjakka5.github.io/Concord/). These are still work in progress and might be incomplete though. + +## Installation +Download the repository and drop it in your project, then simply require it as: +```lua +local Concord = require(PathToConcord).init() + +You will only need to call .init once when you first require it. +``` + +## Modules +Below is a list of modules. +More information about what each done can be found in the Wiki + +```lua +local Concord = require("concord") +local Entity = require("concord.entity") +local Component = require("concord.component") +local System = require("concord.system") +local Instance = require("concord.instance") +``` + +## Contributors +``` +Positive07: Constant support and a good rubberduck +Brbl: Early testing and issue reporting +Josh: Squashed a few bugs and docs +Erasio: Took inspiration from HooECS. Also introduced me to ECS. +``` + +## Licence +MIT Licensed - Copyright Justin van der Leij (Tjakka5) diff --git a/lib/assemblage.lua b/lib/assemblage.lua index 67a1428..79b3cbb 100644 --- a/lib/assemblage.lua +++ b/lib/assemblage.lua @@ -10,8 +10,6 @@ function Assemblage.new(assemble) __isAssemblage = true, }, Assemblage) - Assemblage.__mt = {__index = assemblage} - return assemblage end @@ -22,5 +20,7 @@ function Assemblage:assemble(e, ...) end return setmetatable(Assemblage, { - __call = function(_, ...) return Assemblage.new(...) end, + __call = function(_, ...) + return Assemblage.new(...) + end, }) diff --git a/lib/component.lua b/lib/component.lua index 5696968..63cb1fb 100644 --- a/lib/component.lua +++ b/lib/component.lua @@ -33,5 +33,7 @@ function Component:__initialize(...) end return setmetatable(Component, { - __call = function(_, ...) return Component.new(...) end, + __call = function(_, ...) + return Component.new(...) + end, }) diff --git a/lib/entity.lua b/lib/entity.lua index 441b9df..9cabe33 100644 --- a/lib/entity.lua +++ b/lib/entity.lua @@ -136,5 +136,7 @@ function Entity:has(component) end return setmetatable(Entity, { - __call = function(_, ...) return Entity.new(...) end, + __call = function(_, ...) + return Entity.new(...) + end, }) diff --git a/lib/init.lua b/lib/init.lua index 81887fb..61eb5e8 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -61,9 +61,11 @@ function Concord.init(settings) error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) end - for i, instance in ipairs(Concord.instances) do - table.remove(Concord.instances, i) - break + for i, _instance in ipairs(Concord.instances) do + if (instance == _instance) then + table.remove(Concord.instances, i) + break + end end end diff --git a/lib/instance.lua b/lib/instance.lua index fa59c35..571bfa4 100644 --- a/lib/instance.lua +++ b/lib/instance.lua @@ -282,5 +282,7 @@ function Instance:onEntityRemoved(e) -- luacheck: ignore end return setmetatable(Instance, { - __call = function(_, ...) return Instance.new(...) end, + __call = function(_, ...) + return Instance.new(...) + end, }) diff --git a/lib/list.lua b/lib/list.lua index a5698c9..a79d20e 100644 --- a/lib/list.lua +++ b/lib/list.lua @@ -77,5 +77,7 @@ function List:has(obj) end return setmetatable(List, { - __call = function() return List.new() end, + __call = function() + return List.new() + end, }) diff --git a/lib/pool.lua b/lib/pool.lua index e7e949c..779efd9 100644 --- a/lib/pool.lua +++ b/lib/pool.lua @@ -46,5 +46,7 @@ end return setmetatable(Pool, { __index = List, - __call = function(_, ...) return Pool.new(...) end, + __call = function(_, ...) + return Pool.new(...) + end, }) diff --git a/lib/system.lua b/lib/system.lua index a42f79b..4572aa6 100644 --- a/lib/system.lua +++ b/lib/system.lua @@ -156,5 +156,7 @@ function System:disabledCallback(callbackName) -- luacheck: ignore end return setmetatable(System, { - __call = function(_, ...) return System.new(...) end, + __call = function(_, ...) + return System.new(...) + end, }) From 39ec2106b7c0ef245885b4a92669ccd3ed9f0b5c Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 26 Nov 2018 12:43:48 +0100 Subject: [PATCH 004/113] Removed .init --- examples/assemblageTest/init.lua | 6 ++-- examples/baseLayout/main.lua | 6 ++-- examples/simpleDrawing/init.lua | 15 ++++++--- lib/init.lua | 48 +++------------------------- lib/run.lua | 55 -------------------------------- lib/type.lua | 2 ++ 6 files changed, 22 insertions(+), 110 deletions(-) delete mode 100644 lib/run.lua diff --git a/examples/assemblageTest/init.lua b/examples/assemblageTest/init.lua index 730129b..444d37b 100644 --- a/examples/assemblageTest/init.lua +++ b/examples/assemblageTest/init.lua @@ -1,13 +1,11 @@ -local Concord = require("lib").init({ - useEvents = true -}) +local Concord = require("lib") + local Entity = Concord.entity local Component = Concord.component local System = Concord.system local Assemblage = Concord.assemblage local Game = Concord.instance() -Concord.addInstance(Game) local Legs = Component(function(e, legCount) e.legCount = legCount or 0 diff --git a/examples/baseLayout/main.lua b/examples/baseLayout/main.lua index ed6e30d..b4785c2 100644 --- a/examples/baseLayout/main.lua +++ b/examples/baseLayout/main.lua @@ -1,8 +1,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Concord = require("lib").init({ - useEvents = true -}) +local Concord = require("lib") local C = require(PATH..".src.components") -local S = require(PATH..".src.systems") \ No newline at end of file +local S = require(PATH..".src.systems") diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index 8cc0426..5eca907 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -1,12 +1,10 @@ -local Concord = require("lib").init({ - useEvents = true -}) +local Concord = require("lib") + local Entity = Concord.entity local Component = Concord.component local System = Concord.system local Game = Concord.instance() -Concord.addInstance(Game) local Position = Component(function(e, x, y) e.x = x @@ -118,3 +116,12 @@ for _ = 1, 100 do Game:addEntity(e) end + + +function love.update(dt) + Game:emit("update", dt) +end + +function love.draw() + Game:emit("draw") +end diff --git a/lib/init.lua b/lib/init.lua index 61eb5e8..528dd91 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -2,8 +2,6 @@ local PATH = (...):gsub('%.init$', '') -local Type = require(PATH..".type") - local Concord = { _VERSION = "1.0", _DESCRIPTION = "A feature-complete ECS library", @@ -33,46 +31,10 @@ local Concord = { ]] } ---- Initializes the library with some optional settings --- @param settings Table of settings: { --- useEvents Flag to overwrite love.run and use events. Defaults to false --- } --- @return Concord -function Concord.init(settings) - Concord.entity = require(PATH..".entity") - Concord.component = require(PATH..".component") - Concord.system = require(PATH..".system") - Concord.instance = require(PATH..".instance") - Concord.assemblage = require(PATH..".assemblage") - - if settings and settings.useEvents then - Concord.instances = {} - - Concord.addInstance = function(instance) - if not Type.isInstance(instance) then - error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) - end - - table.insert(Concord.instances, instance) - end - - Concord.removeInstance = function(instance) - if not Type.isInstance(instance) then - error("bad argument #1 to 'Concord.addInstance' (Instance expected, got "..type(instance)..")", 2) - end - - for i, _instance in ipairs(Concord.instances) do - if (instance == _instance) then - table.remove(Concord.instances, i) - break - end - end - end - - love.run = require(PATH..".run") - end - - return Concord -end +Concord.entity = require(PATH..".entity") +Concord.component = require(PATH..".component") +Concord.system = require(PATH..".system") +Concord.instance = require(PATH..".instance") +Concord.assemblage = require(PATH..".assemblage") return Concord diff --git a/lib/run.lua b/lib/run.lua deleted file mode 100644 index 5ad1ce7..0000000 --- a/lib/run.lua +++ /dev/null @@ -1,55 +0,0 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local Concord = require(PATH) - -return function() - if love.math then - love.math.setRandomSeed(os.time()) - love.timer.step() - end - - for _, instance in ipairs(Concord.instances) do - instance:emit("load", arg) - end - - if love.timer then love.timer.step() end - - local dt = 0 - - return function() - if love.event then - love.event.pump() - for name, a, b, c, d, e, f in love.event.poll() do - for _, instance in ipairs(Concord.instances) do - instance:emit(name, a, b, c, d, e, f) - end - - if name == "quit" then - return a or 0 - end - end - end - - if love.timer then - love.timer.step() - dt = love.timer.getDelta() - end - - for _, instance in ipairs(Concord.instances) do - instance:emit("update", dt) - end - - if love.graphics and love.graphics.isActive() then - love.graphics.clear(love.graphics.getBackgroundColor()) - love.graphics.origin() - - for _, instance in ipairs(Concord.instances) do - instance:emit("draw") - end - - love.graphics.present() - end - - if love.timer then love.timer.sleep(0.001) end - end -end \ No newline at end of file diff --git a/lib/type.lua b/lib/type.lua index 80d14b0..ba52b02 100644 --- a/lib/type.lua +++ b/lib/type.lua @@ -1,3 +1,5 @@ +-- Type + local Type = {} function Type.isComponent(t) From 89a3a7fa8a94c603eacfcf1c9ac3189e7f029f86 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 26 Nov 2018 12:45:24 +0100 Subject: [PATCH 005/113] Rename 'lib' directory to 'src' --- {lib => src}/assemblage.lua | 0 {lib => src}/component.lua | 0 {lib => src}/entity.lua | 0 {lib => src}/init.lua | 0 {lib => src}/instance.lua | 0 {lib => src}/list.lua | 0 {lib => src}/pool.lua | 0 {lib => src}/system.lua | 0 {lib => src}/type.lua | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {lib => src}/assemblage.lua (100%) rename {lib => src}/component.lua (100%) rename {lib => src}/entity.lua (100%) rename {lib => src}/init.lua (100%) rename {lib => src}/instance.lua (100%) rename {lib => src}/list.lua (100%) rename {lib => src}/pool.lua (100%) rename {lib => src}/system.lua (100%) rename {lib => src}/type.lua (100%) diff --git a/lib/assemblage.lua b/src/assemblage.lua similarity index 100% rename from lib/assemblage.lua rename to src/assemblage.lua diff --git a/lib/component.lua b/src/component.lua similarity index 100% rename from lib/component.lua rename to src/component.lua diff --git a/lib/entity.lua b/src/entity.lua similarity index 100% rename from lib/entity.lua rename to src/entity.lua diff --git a/lib/init.lua b/src/init.lua similarity index 100% rename from lib/init.lua rename to src/init.lua diff --git a/lib/instance.lua b/src/instance.lua similarity index 100% rename from lib/instance.lua rename to src/instance.lua diff --git a/lib/list.lua b/src/list.lua similarity index 100% rename from lib/list.lua rename to src/list.lua diff --git a/lib/pool.lua b/src/pool.lua similarity index 100% rename from lib/pool.lua rename to src/pool.lua diff --git a/lib/system.lua b/src/system.lua similarity index 100% rename from lib/system.lua rename to src/system.lua diff --git a/lib/type.lua b/src/type.lua similarity index 100% rename from lib/type.lua rename to src/type.lua From 26bd0ef93732812f2e760f32e132c501a5725170 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 26 Nov 2018 12:58:41 +0100 Subject: [PATCH 006/113] Rename 'instance' to 'context' --- examples/assemblageTest/init.lua | 4 +- examples/baseLayout/main.lua | 2 +- examples/simpleDrawing/init.lua | 4 +- src/{instance.lua => context.lua} | 102 +++++++++++++++--------------- src/entity.lua | 10 +-- src/init.lua | 2 +- src/system.lua | 16 ++--- src/type.lua | 4 +- 8 files changed, 72 insertions(+), 72 deletions(-) rename src/{instance.lua => context.lua} (67%) diff --git a/examples/assemblageTest/init.lua b/examples/assemblageTest/init.lua index 444d37b..01adb8b 100644 --- a/examples/assemblageTest/init.lua +++ b/examples/assemblageTest/init.lua @@ -1,11 +1,11 @@ -local Concord = require("lib") +local Concord = require("src") local Entity = Concord.entity local Component = Concord.component local System = Concord.system local Assemblage = Concord.assemblage -local Game = Concord.instance() +local Game = Concord.context() local Legs = Component(function(e, legCount) e.legCount = legCount or 0 diff --git a/examples/baseLayout/main.lua b/examples/baseLayout/main.lua index b4785c2..6d6717f 100644 --- a/examples/baseLayout/main.lua +++ b/examples/baseLayout/main.lua @@ -1,6 +1,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Concord = require("lib") +local Concord = require("src") local C = require(PATH..".src.components") local S = require(PATH..".src.systems") diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index 5eca907..cbf7ac5 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -1,10 +1,10 @@ -local Concord = require("lib") +local Concord = require("src") local Entity = Concord.entity local Component = Concord.component local System = Concord.system -local Game = Concord.instance() +local Game = Concord.context() local Position = Component(function(e, x, y) e.x = x diff --git a/src/instance.lua b/src/context.lua similarity index 67% rename from src/instance.lua rename to src/context.lua index 571bfa4..3c9e931 100644 --- a/src/instance.lua +++ b/src/context.lua @@ -1,17 +1,17 @@ ---- Instance +--- Context local PATH = (...):gsub('%.[^%.]+$', '') local Type = require(PATH..".type") local List = require(PATH..".list") -local Instance = {} -Instance.__index = Instance +local Context = {} +Context.__index = Context ---- Creates a new Instance. --- @return The new instance -function Instance.new() - local instance = setmetatable({ +--- Creates a new Context. +-- @return The new context +function Context.new() + local context = setmetatable({ entities = List(), systems = List(), events = {}, @@ -19,35 +19,35 @@ function Instance.new() marked = {}, removed = {}, - __isInstance = true, - }, Instance) + __isContext = true, + }, Context) - return instance + return context end ---- Adds an Entity to the Instance. +--- Adds an Entity to the Context. -- @param e The Entity to add -- @return self -function Instance:addEntity(e) +function Context:addEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:addEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'Context:addEntity' (Entity expected, got "..type(e)..")", 2) end self:onEntityAdded(e) - e.instances:add(self) + e.contexts:add(self) self.entities:add(e) self:checkEntity(e) return self end ---- Marks an Entity as removed from the Instance. +--- Marks an Entity as removed from the Context. -- @param e The Entity to mark -- @return self -function Instance:removeEntity(e) +function Context:removeEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'Context:removeEntity' (Entity expected, got "..type(e)..")", 2) end self.removed[#self.removed + 1] = e @@ -55,9 +55,9 @@ function Instance:removeEntity(e) return self end -function Instance:markEntity(e) +function Context:markEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:markEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'Context:markEntity' (Entity expected, got "..type(e)..")", 2) end self.marked[#self.marked + 1] = e @@ -65,12 +65,12 @@ function Instance:markEntity(e) return self end ---- Checks an Entity against all the systems in the Instance. +--- Checks an Entity against all the systems in the Context. -- @param e The Entity to check -- @return self -function Instance:checkEntity(e) +function Context:checkEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Instance:checkEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'Context:checkEntity' (Entity expected, got "..type(e)..")", 2) end for i = 1, self.systems.size do @@ -80,9 +80,9 @@ function Instance:checkEntity(e) return self end ---- Completely removes all marked Entities in the Instance. +--- Completely removes all marked Entities in the Context. -- @return self -function Instance:flush() +function Context:flush() while #self.marked > 0 do local marked = self.removed self.removed = {} @@ -90,8 +90,8 @@ function Instance:flush() for i = 1, #marked do local e = marked[i] - e.instances:apply() - e.instances:checkEntity(e) + e.contexts:apply() + e.contexts:checkEntity(e) end end @@ -102,7 +102,7 @@ function Instance:flush() for i = 1, #removed do local e = removed[i] - e.instances:remove(self) + e.contexts:remove(self) self.entities:remove(e) for j = 1, self.systems.size do @@ -122,24 +122,24 @@ function Instance:flush() return self end ---- Adds a System to the Instance. +--- Adds a System to the Context. -- @param system The System to add -- @param eventName The Event to register to -- @param callback The function name to call. Defaults to eventName -- @param enabled If the system is enabled. Defaults to true -- @return self -function Instance:addSystem(system, eventName, callback, enabled) +function Context:addSystem(system, eventName, callback, enabled) if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:addSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'Context:addSystem' (System expected, got "..type(system)..")", 2) end - if system.__instance and system.__instance ~= self then - error("System already in instance '" ..tostring(system.__instance).."'") + if system.__context and system.__context ~= self then + error("System already in context '" ..tostring(system.__context).."'") end if not self.systems:has(system) then self.systems:add(system) - system.__instance = self + system.__context = self system:addedTo(self) end @@ -169,41 +169,41 @@ function Instance:addSystem(system, eventName, callback, enabled) return self end ---- Enables a System in the Instance. +--- Enables a System in the Context. -- @param system The System to enable -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function Instance:enableSystem(system, eventName, callback) +function Context:enableSystem(system, eventName, callback) if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:enableSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'Context:enableSystem' (System expected, got "..type(system)..")", 2) end return self:setSystem(system, eventName, callback, true) end ---- Disables a System in the Instance. +--- Disables a System in the Context. -- @param system The System to disable -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function Instance:disableSystem(system, eventName, callback) +function Context:disableSystem(system, eventName, callback) if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:disableSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'Context:disableSystem' (System expected, got "..type(system)..")", 2) end return self:setSystem(system, eventName, callback, false) end ---- Sets a System 'enable' in the Instance. +--- Sets a System 'enable' in the Context. -- @param system The System to set -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @param enable The state to set it to -- @return self -function Instance:setSystem(system, eventName, callback, enable) +function Context:setSystem(system, eventName, callback, enable) if not Type.isSystem(system) then - error("bad argument #1 to 'Instance:setSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'Context:setSystem' (System expected, got "..type(system)..")", 2) end callback = callback or eventName @@ -233,13 +233,13 @@ function Instance:setSystem(system, eventName, callback, enable) return self end ---- Emits an Event in the Instance. +--- Emits an Event in the Context. -- @param eventName The Event that should be emitted -- @param ... Parameters passed to listeners -- @return self -function Instance:emit(eventName, ...) +function Context:emit(eventName, ...) if not eventName or type(eventName) ~= "string" then - error("bad argument #1 to 'Instance:emit' (String expected, got "..type(eventName)..")") + error("bad argument #1 to 'Context:emit' (String expected, got "..type(eventName)..")") end self:flush() @@ -259,9 +259,9 @@ function Instance:emit(eventName, ...) return self end ---- Removes all entities from the Instance +--- Removes all entities from the Context -- @return self -function Instance:clear() +function Context:clear() for i = 1, self.entities.size do self.entities:get(i):destroy() end @@ -273,16 +273,16 @@ end --- Default callback for adding an Entity. -- @param e The Entity that was added -function Instance:onEntityAdded(e) -- luacheck: ignore +function Context:onEntityAdded(e) -- luacheck: ignore end --- Default callback for removing an Entity. -- @param e The Entity that was removed -function Instance:onEntityRemoved(e) -- luacheck: ignore +function Context:onEntityRemoved(e) -- luacheck: ignore end -return setmetatable(Instance, { +return setmetatable(Context, { __call = function(_, ...) - return Instance.new(...) + return Context.new(...) end, }) diff --git a/src/entity.lua b/src/entity.lua index 9cabe33..0f2da8f 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -13,7 +13,7 @@ Entity.__index = Entity function Entity.new() local e = setmetatable({ removed = {}, - instances = List(), + contexts = List(), __isEntity = true, }, Entity) @@ -86,8 +86,8 @@ function Entity:assemble(assemblage, ...) end function Entity:mark() - for i = 1, self.instances.size do - self.instances:get(i):markEntity(self) + for i = 1, self.contexts.size do + self.contexts:get(i):markEntity(self) end end @@ -106,8 +106,8 @@ end --- Destroys the Entity. -- @return self function Entity:destroy() - for i = 1, self.instances.size do - self.instances:get(i):removeEntity(self) + for i = 1, self.contexts.size do + self.contexts:get(i):removeEntity(self) end return self diff --git a/src/init.lua b/src/init.lua index 528dd91..ad55f50 100644 --- a/src/init.lua +++ b/src/init.lua @@ -34,7 +34,7 @@ local Concord = { Concord.entity = require(PATH..".entity") Concord.component = require(PATH..".component") Concord.system = require(PATH..".system") -Concord.instance = require(PATH..".instance") +Concord.context = require(PATH..".context") Concord.assemblage = require(PATH..".assemblage") return Concord diff --git a/src/system.lua b/src/system.lua index 4572aa6..2eebe28 100644 --- a/src/system.lua +++ b/src/system.lua @@ -11,7 +11,7 @@ System.mt = { local system = setmetatable({ __all = {}, __pools = {}, - __instance = nil, + __context = nil, __isSystem = true, }, systemProto) @@ -129,10 +129,10 @@ function System:clear() end end ---- Returns the Instance the System is in. --- @return The Instance -function System:getInstance() - return self.__instance +--- Returns the Context the System is in. +-- @return The Context +function System:getContext() + return self.__context end --- Default callback for system initialization. @@ -140,9 +140,9 @@ end function System:init(...) -- luacheck: ignore end --- Default callback for when the System is added to an Instance. --- @param instance The Instance the System was added to -function System:addedTo(instance) -- luacheck: ignore +-- Default callback for when the System is added to an Context. +-- @param context The Context the System was added to +function System:addedTo(context) -- luacheck: ignore end -- Default callback for when a System's callback is enabled. diff --git a/src/type.lua b/src/type.lua index ba52b02..999c42c 100644 --- a/src/type.lua +++ b/src/type.lua @@ -14,8 +14,8 @@ function Type.isSystem(t) return type(t) == "table" and t.__isSystem end -function Type.isInstance(t) - return type(t) == "table" and t.__isInstance +function Type.isContext(t) + return type(t) == "table" and t.__isContext end function Type.isAssemblage(t) From 83162ec02c9ff0ca8eb8211dfa1d612c7f80e603 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Thu, 29 Nov 2018 22:04:50 +0100 Subject: [PATCH 007/113] Rename 'context' to 'world' --- examples/simpleDrawing/init.lua | 4 +- src/entity.lua | 11 ++-- src/init.lua | 4 +- src/system.lua | 20 +++---- src/{context.lua => world.lua} | 102 ++++++++++++++++---------------- 5 files changed, 69 insertions(+), 72 deletions(-) rename src/{context.lua => world.lua} (66%) diff --git a/examples/simpleDrawing/init.lua b/examples/simpleDrawing/init.lua index cbf7ac5..29b31d1 100644 --- a/examples/simpleDrawing/init.lua +++ b/examples/simpleDrawing/init.lua @@ -4,7 +4,7 @@ local Entity = Concord.entity local Component = Concord.component local System = Concord.system -local Game = Concord.context() +local Game = Concord.world() local Position = Component(function(e, x, y) e.x = x @@ -48,8 +48,6 @@ function CircleRenderer:flush() for _, e in ipairs(self.pool.removed) do print(tostring(e).. " was removed from my pool D:") end - - self:clear() end function CircleRenderer:draw() diff --git a/src/entity.lua b/src/entity.lua index 0f2da8f..62d64c4 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -13,7 +13,7 @@ Entity.__index = Entity function Entity.new() local e = setmetatable({ removed = {}, - contexts = List(), + worlds = List(), __isEntity = true, }, Entity) @@ -86,8 +86,8 @@ function Entity:assemble(assemblage, ...) end function Entity:mark() - for i = 1, self.contexts.size do - self.contexts:get(i):markEntity(self) + for i = 1, self.worlds.size do + self.worlds:get(i):markEntity(self) end end @@ -96,7 +96,6 @@ function Entity:apply() local component = self.removed[i] self[component] = nil - self.removed[i] = nil end @@ -106,8 +105,8 @@ end --- Destroys the Entity. -- @return self function Entity:destroy() - for i = 1, self.contexts.size do - self.contexts:get(i):removeEntity(self) + for i = 1, self.worlds.size do + self.worlds:get(i):removeEntity(self) end return self diff --git a/src/init.lua b/src/init.lua index ad55f50..e13f190 100644 --- a/src/init.lua +++ b/src/init.lua @@ -3,7 +3,7 @@ local PATH = (...):gsub('%.init$', '') local Concord = { - _VERSION = "1.0", + _VERSION = "2.0 Beta", _DESCRIPTION = "A feature-complete ECS library", _LICENCE = [[ MIT LICENSE @@ -34,7 +34,7 @@ local Concord = { Concord.entity = require(PATH..".entity") Concord.component = require(PATH..".component") Concord.system = require(PATH..".system") -Concord.context = require(PATH..".context") +Concord.world = require(PATH..".world") Concord.assemblage = require(PATH..".assemblage") return Concord diff --git a/src/system.lua b/src/system.lua index 2eebe28..10729da 100644 --- a/src/system.lua +++ b/src/system.lua @@ -9,9 +9,9 @@ System.mt = { __index = System, __call = function(systemProto, ...) local system = setmetatable({ - __all = {}, - __pools = {}, - __context = nil, + __all = {}, + __pools = {}, + __world = nil, __isSystem = true, }, systemProto) @@ -129,10 +129,10 @@ function System:clear() end end ---- Returns the Context the System is in. --- @return The Context -function System:getContext() - return self.__context +--- Returns the World the System is in. +-- @return The world the system is in +function System:getWorld() + return self.__world end --- Default callback for system initialization. @@ -140,9 +140,9 @@ end function System:init(...) -- luacheck: ignore end --- Default callback for when the System is added to an Context. --- @param context The Context the System was added to -function System:addedTo(context) -- luacheck: ignore +-- Default callback for when the System is added to an World. +-- @param world The World the System was added to +function System:addedTo(World) -- luacheck: ignore end -- Default callback for when a System's callback is enabled. diff --git a/src/context.lua b/src/world.lua similarity index 66% rename from src/context.lua rename to src/world.lua index 3c9e931..59305fa 100644 --- a/src/context.lua +++ b/src/world.lua @@ -1,17 +1,17 @@ ---- Context +--- World local PATH = (...):gsub('%.[^%.]+$', '') local Type = require(PATH..".type") local List = require(PATH..".list") -local Context = {} -Context.__index = Context +local World = {} +World.__index = World ---- Creates a new Context. --- @return The new context -function Context.new() - local context = setmetatable({ +--- Creates a new World. +-- @return The new World +function World.new() + local world = setmetatable({ entities = List(), systems = List(), events = {}, @@ -19,35 +19,35 @@ function Context.new() marked = {}, removed = {}, - __isContext = true, - }, Context) + __isWorld = true, + }, World) - return context + return world end ---- Adds an Entity to the Context. +--- Adds an Entity to the World. -- @param e The Entity to add -- @return self -function Context:addEntity(e) +function World:addEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Context:addEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) end self:onEntityAdded(e) - e.contexts:add(self) + e.worlds:add(self) self.entities:add(e) self:checkEntity(e) return self end ---- Marks an Entity as removed from the Context. +--- Marks an Entity as removed from the World. -- @param e The Entity to mark -- @return self -function Context:removeEntity(e) +function World:removeEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Context:removeEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) end self.removed[#self.removed + 1] = e @@ -55,9 +55,9 @@ function Context:removeEntity(e) return self end -function Context:markEntity(e) +function World:markEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Context:markEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'World:markEntity' (Entity expected, got "..type(e)..")", 2) end self.marked[#self.marked + 1] = e @@ -65,12 +65,12 @@ function Context:markEntity(e) return self end ---- Checks an Entity against all the systems in the Context. +--- Checks an Entity against all the systems in the World. -- @param e The Entity to check -- @return self -function Context:checkEntity(e) +function World:checkEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'Context:checkEntity' (Entity expected, got "..type(e)..")", 2) + error("bad argument #1 to 'World:checkEntity' (Entity expected, got "..type(e)..")", 2) end for i = 1, self.systems.size do @@ -80,9 +80,9 @@ function Context:checkEntity(e) return self end ---- Completely removes all marked Entities in the Context. +--- Completely removes all marked Entities in the World. -- @return self -function Context:flush() +function World:flush() while #self.marked > 0 do local marked = self.removed self.removed = {} @@ -90,8 +90,8 @@ function Context:flush() for i = 1, #marked do local e = marked[i] - e.contexts:apply() - e.contexts:checkEntity(e) + e.Worlds:apply() + e.Worlds:checkEntity(e) end end @@ -102,7 +102,7 @@ function Context:flush() for i = 1, #removed do local e = removed[i] - e.contexts:remove(self) + e.worlds:remove(self) self.entities:remove(e) for j = 1, self.systems.size do @@ -122,24 +122,24 @@ function Context:flush() return self end ---- Adds a System to the Context. +--- Adds a System to the World. -- @param system The System to add -- @param eventName The Event to register to -- @param callback The function name to call. Defaults to eventName -- @param enabled If the system is enabled. Defaults to true -- @return self -function Context:addSystem(system, eventName, callback, enabled) +function World:addSystem(system, eventName, callback, enabled) if not Type.isSystem(system) then - error("bad argument #1 to 'Context:addSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'World:addSystem' (System expected, got "..type(system)..")", 2) end - if system.__context and system.__context ~= self then - error("System already in context '" ..tostring(system.__context).."'") + if system.__World and system.__World ~= self then + error("System already in World '" ..tostring(system.__World).."'") end if not self.systems:has(system) then self.systems:add(system) - system.__context = self + system.__World = self system:addedTo(self) end @@ -169,41 +169,41 @@ function Context:addSystem(system, eventName, callback, enabled) return self end ---- Enables a System in the Context. +--- Enables a System in the World. -- @param system The System to enable -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function Context:enableSystem(system, eventName, callback) +function World:enableSystem(system, eventName, callback) if not Type.isSystem(system) then - error("bad argument #1 to 'Context:enableSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'World:enableSystem' (System expected, got "..type(system)..")", 2) end return self:setSystem(system, eventName, callback, true) end ---- Disables a System in the Context. +--- Disables a System in the World. -- @param system The System to disable -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function Context:disableSystem(system, eventName, callback) +function World:disableSystem(system, eventName, callback) if not Type.isSystem(system) then - error("bad argument #1 to 'Context:disableSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'World:disableSystem' (System expected, got "..type(system)..")", 2) end return self:setSystem(system, eventName, callback, false) end ---- Sets a System 'enable' in the Context. +--- Sets a System 'enable' in the World. -- @param system The System to set -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @param enable The state to set it to -- @return self -function Context:setSystem(system, eventName, callback, enable) +function World:setSystem(system, eventName, callback, enable) if not Type.isSystem(system) then - error("bad argument #1 to 'Context:setSystem' (System expected, got "..type(system)..")", 2) + error("bad argument #1 to 'World:setSystem' (System expected, got "..type(system)..")", 2) end callback = callback or eventName @@ -233,13 +233,13 @@ function Context:setSystem(system, eventName, callback, enable) return self end ---- Emits an Event in the Context. +--- Emits an Event in the World. -- @param eventName The Event that should be emitted -- @param ... Parameters passed to listeners -- @return self -function Context:emit(eventName, ...) +function World:emit(eventName, ...) if not eventName or type(eventName) ~= "string" then - error("bad argument #1 to 'Context:emit' (String expected, got "..type(eventName)..")") + error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")") end self:flush() @@ -259,9 +259,9 @@ function Context:emit(eventName, ...) return self end ---- Removes all entities from the Context +--- Removes all entities from the World -- @return self -function Context:clear() +function World:clear() for i = 1, self.entities.size do self.entities:get(i):destroy() end @@ -273,16 +273,16 @@ end --- Default callback for adding an Entity. -- @param e The Entity that was added -function Context:onEntityAdded(e) -- luacheck: ignore +function World:onEntityAdded(e) -- luacheck: ignore end --- Default callback for removing an Entity. -- @param e The Entity that was removed -function Context:onEntityRemoved(e) -- luacheck: ignore +function World:onEntityRemoved(e) -- luacheck: ignore end -return setmetatable(Context, { +return setmetatable(World, { __call = function(_, ...) - return Context.new(...) + return World.new(...) end, }) From 052e1cd2ce611be1842e15e890b835efac750b4a Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Wed, 8 May 2019 11:44:15 +0200 Subject: [PATCH 008/113] Add example games --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d2187ef..a46947c 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,17 @@ local System = require("concord.system") local Instance = require("concord.instance") ``` +## Example games +[A Cat Game](https://github.com/flamendless/ECS-A-Cat-Game) by Brbl +[Tetris](https://github.com/pikashira/tetris-love-ecs) by Pikashira + ## Contributors ``` Positive07: Constant support and a good rubberduck Brbl: Early testing and issue reporting Josh: Squashed a few bugs and docs Erasio: Took inspiration from HooECS. Also introduced me to ECS. +Brbl, Pikashria: Example games ``` ## Licence From 93da73ad6ceb93e40c5dfa29ac4f2d2d4c01db16 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Wed, 8 May 2019 11:44:42 +0200 Subject: [PATCH 009/113] Formatting is hard --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a46947c..4ef32d5 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ local Instance = require("concord.instance") ## Example games [A Cat Game](https://github.com/flamendless/ECS-A-Cat-Game) by Brbl + [Tetris](https://github.com/pikashira/tetris-love-ecs) by Pikashira ## Contributors From cc6783ac31b7d5b6930c7c539d214529879a6ea4 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Tue, 21 May 2019 19:08:43 +0100 Subject: [PATCH 010/113] Fixed a broken luadoc comment Rewriting this code was haaaard, man... --- lib/list.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/list.lua b/lib/list.lua index 33b2c10..be70e4d 100644 --- a/lib/list.lua +++ b/lib/list.lua @@ -68,7 +68,7 @@ end --- Gets if the List has the object. -- @param obj The object to search for --- true if the list has the object, false otherwise +-- @return true if the list has the object, false otherwise function List:has(obj) return self.pointers[obj] and true end From f7e1be8e1d685b8bfd29835dc2f36229e30c3ad7 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Tue, 10 Dec 2019 12:22:15 +0100 Subject: [PATCH 011/113] Remove todo list --- TODO | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index 33fc816..0000000 --- a/TODO +++ /dev/null @@ -1,15 +0,0 @@ -[x] Modify pools and list to be more efficient -[x] Rework Entity:give to remove any previous component and re-add it. -[x] Add Entity:ensure (maybe?) -[ ] Put pools in the Instance and invert dependency. -[ ] Share pools between systems -[x] Remove System callbacks -[x] Put .added and .removed in pools so they can be iterated over -[x] Implement assemblages -[x] Do :apply automatically with marked entities -[x] Remove Entity.components. Use Entity[Component] instead - -[ ] Add missing documentation -[ ] Fix current documentation -[ ] Write unit tests -[ ] Write examples From bc47eaa651f41c3ca0f91673761efc0ff383a7cb Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Wed, 11 Dec 2019 13:04:09 +0100 Subject: [PATCH 012/113] Allow for named components --- main.lua | 37 +++++++++++++++++++++++++++++++++++++ src/component.lua | 17 ++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/main.lua b/main.lua index ce78707..cd13075 100644 --- a/main.lua +++ b/main.lua @@ -1,4 +1,41 @@ +--[=[ local file = "examples.simpleDrawing" -- local file = "examples.baseLayout.main" require(file) +]=]-- + +local Concord = require("src") +local Component = require("src.component") + +local test_comp_1 = Concord.component("test_comp_1", function(e, x, y) + e.x = x + e.y = y +end) + +local test_comp_2 = Concord.component("test_comp_2", function(e, a) + e.a = a +end) + +local test_comp_3 = Concord.component("test_comp_3", function(e, b) + e.b = b +end) + +local test_system = Concord.system({Component.test_comp_1}) +function test_system:update(dt) + print(#self.pool) +end + +local world = Concord.world() + +local entity = Concord.entity() +entity:give(Component.test_comp_2, 100, 100) +entity:apply() + +world:addEntity(entity) + +world:addSystem(test_system(), "update") + +function love.update(dt) + world:emit("update", dt) +end \ No newline at end of file diff --git a/src/component.lua b/src/component.lua index 63cb1fb..5777743 100644 --- a/src/component.lua +++ b/src/component.lua @@ -6,8 +6,21 @@ Component.__index = Component --- Creates a new Component. -- @param populate A function that populates the Bag with values -- @return A Component object -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 not (populate == nil or type(populate) == "function") then + error("bad argument #2 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) + end + + if (Component[name] ~= nil) then + error("bad argument #2 to 'Component.new' (Component with name '"..name.."' already exists", 2) + end + local component = setmetatable({ + __name = name, __populate = populate, __isComponent = true, @@ -15,6 +28,8 @@ function Component.new(populate) component.__mt = {__index = component} + Component[name] = component + return component end From 038111d55825d7a1347c51cbbf41a3d2ef331926 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Thu, 19 Dec 2019 08:47:38 +0100 Subject: [PATCH 013/113] Streamline entity lifetime --- main.lua | 37 ++++++++++++++-- src/component.lua | 2 +- src/entity.lua | 45 +++++++------------ src/list.lua | 2 +- src/pool.lua | 27 ++++++----- src/system.lua | 56 ++++------------------- src/world.lua | 111 +++++++++++++++++----------------------------- 7 files changed, 117 insertions(+), 163 deletions(-) diff --git a/main.lua b/main.lua index cd13075..dad60e1 100644 --- a/main.lua +++ b/main.lua @@ -22,20 +22,49 @@ local test_comp_3 = Concord.component("test_comp_3", function(e, b) end) local test_system = Concord.system({Component.test_comp_1}) -function test_system:update(dt) - print(#self.pool) + +function onEntityAdded(e) + print("Added") end +function onEntityRemoved(e) + print("Removed") +end + +function test_system:init() + self.pool.onEntityAdded = onEntityAdded + self.pool.onEntityRemoved = onEntityRemoved +end + +function test_system:update(dt) + --print(#self.pool) +end + + + local world = Concord.world() local entity = Concord.entity() -entity:give(Component.test_comp_2, 100, 100) -entity:apply() +entity:give(Component.test_comp_1, 100, 100) world:addEntity(entity) world:addSystem(test_system(), "update") function love.update(dt) + world:flush() + world:emit("update", dt) +end + +function love.keypressed(key) + if key == "q" then + entity:remove(Component.test_comp_1) + end + if key == "w" then + entity:give(Component.test_comp_1) + end + if key == "e" then + world:removeEntity(entity) + end end \ No newline at end of file diff --git a/src/component.lua b/src/component.lua index 5777743..102e722 100644 --- a/src/component.lua +++ b/src/component.lua @@ -51,4 +51,4 @@ return setmetatable(Component, { __call = function(_, ...) return Component.new(...) end, -}) +}) \ No newline at end of file diff --git a/src/entity.lua b/src/entity.lua index 62d64c4..a6c6b29 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -3,7 +3,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') local Type = require(PATH..".type") -local List = require(PATH..".list") local Entity = {} Entity.__index = Entity @@ -12,8 +11,11 @@ Entity.__index = Entity -- @return A new Entity function Entity.new() local e = setmetatable({ - removed = {}, - worlds = List(), + world = nil, + + __isDirty = true, + __wasAdded = false, + __wasRemoved = false, __isEntity = true, }, Entity) @@ -25,7 +27,13 @@ local function give(e, component, ...) local comp = component:__initialize(...) e[component] = comp - e:mark() + e.__isDirty = true +end + +local function remove(e, component) + e[component] = nil + + e.__isDirty = true end --- Gives an Entity a component with values. @@ -37,10 +45,6 @@ function Entity:give(component, ...) error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2) end - if self[component] then - self:remove(component):apply() - end - give(self, component, ...) return self @@ -68,9 +72,7 @@ function Entity:remove(component) error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")") end - self.removed[#self.removed + 1] = component - - self:mark() + remove(self, component) return self end @@ -85,28 +87,11 @@ function Entity:assemble(assemblage, ...) return self end -function Entity:mark() - for i = 1, self.worlds.size do - self.worlds:get(i):markEntity(self) - end -end - -function Entity:apply() - for i = 1, #self.removed do - local component = self.removed[i] - - self[component] = nil - self.removed[i] = nil - end - - return self -end - --- Destroys the Entity. -- @return self function Entity:destroy() - for i = 1, self.worlds.size do - self.worlds:get(i):removeEntity(self) + if self.world then + self.world:removeEntity(self) end return self diff --git a/src/list.lua b/src/list.lua index a79d20e..16819da 100644 --- a/src/list.lua +++ b/src/list.lua @@ -73,7 +73,7 @@ end -- @param obj The object to search for -- true if the list has the object, false otherwise function List:has(obj) - return self[obj] and true + return self[obj] and true or false end return setmetatable(List, { diff --git a/src/pool.lua b/src/pool.lua index 779efd9..e82735a 100644 --- a/src/pool.lua +++ b/src/pool.lua @@ -14,9 +14,6 @@ Pool.__index = Pool function Pool.new(name, filter) local pool = setmetatable(List(), Pool) - pool.added = {} - pool.removed = {} - pool.name = name pool.filter = filter @@ -25,18 +22,12 @@ function Pool.new(name, filter) return pool end -function Pool:flush() - for i = 1, math.max(#self.added, #self.removed) do - self.added[i], self.removed[i] = nil, nil - end -end - --- Checks if an Entity is eligible for the Pool. -- @param e The Entity to check -- @return True if the entity is eligible, false otherwise function Pool:eligible(e) for _, component in ipairs(self.filter) do - if not e[component] or e.removed[component] then + if not e[component] then return false end end @@ -44,6 +35,22 @@ function Pool:eligible(e) return true end +function Pool:add(e) + List.add(self, e) + self:onEntityAdded(e) +end + +function Pool:remove(e) + List.remove(self, e) + self:onEntityRemoved(e) +end + +function Pool:onEntityAdded(e) +end + +function Pool:onEntityRemoved(e) +end + return setmetatable(Pool, { __index = List, __call = function(_, ...) diff --git a/src/system.lua b/src/system.lua index 10729da..b078a50 100644 --- a/src/system.lua +++ b/src/system.lua @@ -9,7 +9,6 @@ System.mt = { __index = System, __call = function(systemProto, ...) local system = setmetatable({ - __all = {}, __pools = {}, __world = nil, @@ -63,22 +62,15 @@ end --- Checks and applies an Entity to the System's pools. -- @param e The Entity to check --- @return True if the Entity was added, false if it was removed. Nil if nothing happend -function System:__check(e) +function System:__evaluate(e) for _, pool in ipairs(self.__pools) do - local poolHas = pool:has(e) + local has = pool:has(e) local eligible = pool:eligible(e) - if not poolHas and eligible then + if not has and eligible then pool:add(e) - pool.added[#pool.added + 1] = e - - self:__tryAdd(e) - elseif poolHas and not eligible then + elseif has and not eligible then pool:remove(e) - pool.removed[#pool.removed + 1] = e - - self:__tryRemove(e) end end end @@ -86,46 +78,16 @@ end --- Remove an Entity from the System. -- @param e The Entity to remove function System:__remove(e) - if self.__all[e] then - for _, pool in ipairs(self.__pools) do - if pool:has(e) then - pool:remove(e) - pool.removed[#pool.removed + 1] = e - end - end - - self.__all[e] = nil - end -end - ---- Tries to add an Entity to the System. --- @param e The Entity to add -function System:__tryAdd(e) - if not self.__all[e] then - self.__all[e] = 0 - end - - self.__all[e] = self.__all[e] + 1 -end - ---- Tries to remove an Entity from the System. --- @param e The Entity to remove -function System:__tryRemove(e) - if self.__all[e] then - self.__all[e] = self.__all[e] - 1 - - if self.__all[e] == 0 then - self.__all[e] = nil + for _, pool in ipairs(self.__pools) do + if pool:has(e) then + pool:remove(e) end end end -function System:flush() -- luacheck: ignore -end - function System:clear() for i = 1, #self.__pools do - self.__pools[i]:flush() + self.__pools[i]:clear() end end @@ -142,7 +104,7 @@ end -- Default callback for when the System is added to an World. -- @param world The World the System was added to -function System:addedTo(World) -- luacheck: ignore +function System:addedTo(world) -- luacheck: ignore end -- Default callback for when a System's callback is enabled. diff --git a/src/world.lua b/src/world.lua index 59305fa..2712cb6 100644 --- a/src/world.lua +++ b/src/world.lua @@ -16,8 +16,8 @@ function World.new() systems = List(), events = {}, - marked = {}, - removed = {}, + __added = {}, + __removed = {}, __isWorld = true, }, World) @@ -33,16 +33,19 @@ function World:addEntity(e) error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) end - self:onEntityAdded(e) + if e.world then + error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2) + end + + e.world = self + e.__wasAdded = true - e.worlds:add(self) self.entities:add(e) - self:checkEntity(e) return self end ---- Marks an Entity as removed from the World. +--- Removes an entity from the World. -- @param e The Entity to mark -- @return self function World:removeEntity(e) @@ -50,73 +53,46 @@ function World:removeEntity(e) error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) end - self.removed[#self.removed + 1] = e + e.__wasRemoved = true return self end -function World:markEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'World:markEntity' (Entity expected, got "..type(e)..")", 2) - end - - self.marked[#self.marked + 1] = e - - return self -end - ---- Checks an Entity against all the systems in the World. --- @param e The Entity to check --- @return self -function World:checkEntity(e) - if not Type.isEntity(e) then - error("bad argument #1 to 'World:checkEntity' (Entity expected, got "..type(e)..")", 2) - end - - for i = 1, self.systems.size do - self.systems:get(i):__check(e) - end - - return self -end - ---- Completely removes all marked Entities in the World. -- @return self function World:flush() - while #self.marked > 0 do - local marked = self.removed - self.removed = {} + local e + for i = 1, self.entities.size do + e = self.entities:get(i) - for i = 1, #marked do - local e = marked[i] + if e.__wasAdded then + e.__wasAdded = false + e.__isDirty = false - e.Worlds:apply() - e.Worlds:checkEntity(e) - end - end - - while #self.removed > 0 do - local removed = self.removed - self.removed = {} - - for i = 1, #removed do - local e = removed[i] - - e.worlds:remove(self) - self.entities:remove(e) - - for j = 1, self.systems.size do - self.systems:get(j):__remove(e) + for i = 1, self.systems.size do + self.systems:get(i):__evaluate(e) end - self:onEntityRemoved(e) + self:onEntityAdded(e) end - end - for i = 1, self.systems.size do - local system = self.systems:get(i) - system:flush() - system:clear() + if e.__wasRemoved then + e.world = nil + self.entities:remove(e) + + for i = 1, self.systems.size do + self.systems:get(i):__remove(e) + end + + e.__wasRemoved = false + end + + if e.__isDirty then + for i = 1, self.systems.size do + self.systems:get(i):__evaluate(e) + end + + e.__isDirty = false + end end return self @@ -142,6 +118,10 @@ function World:addSystem(system, eventName, callback, enabled) system.__World = self system:addedTo(self) + + for i = 1, self.entities.size do + system:__evaluate(self.entities:get(i)) + end end if eventName then @@ -159,13 +139,6 @@ function World:addSystem(system, eventName, callback, enabled) end end - local e - for i = 1, self.entities.size do - e = self.entities:get(i) - - self:checkEntity(e) - end - return self end @@ -242,8 +215,6 @@ function World:emit(eventName, ...) error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")") end - self:flush() - local listeners = self.events[eventName] if listeners then From d4efca976c8669f0ab27b0759e7d73b826249d74 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Thu, 19 Dec 2019 10:34:02 +0100 Subject: [PATCH 014/113] Added named systems --- main.lua | 45 +++++++++++++++-------- src/component.lua | 12 +++--- src/components.lua | 29 +++++++++++++++ src/init.lua | 2 + src/list.lua | 2 + src/pool.lua | 4 +- src/system.lua | 37 +++++++++++-------- src/systems.lua | 29 +++++++++++++++ src/type.lua | 16 ++++---- src/world.lua | 92 ++++++++++++++++++++++++---------------------- 10 files changed, 176 insertions(+), 92 deletions(-) create mode 100644 src/components.lua create mode 100644 src/systems.lua diff --git a/main.lua b/main.lua index dad60e1..26ef1f8 100644 --- a/main.lua +++ b/main.lua @@ -6,50 +6,63 @@ require(file) ]=]-- local Concord = require("src") -local Component = require("src.component") -local test_comp_1 = Concord.component("test_comp_1", function(e, x, y) +local Component = require("src.component") +local Components = require("src.components") + +local System = Concord.system +local Systems = Concord.systems + +local Entity = Concord.entity + +local World = Concord.world + +Component("test_comp_1", function(e, x, y) e.x = x e.y = y end) -local test_comp_2 = Concord.component("test_comp_2", function(e, a) +Component("test_comp_2", function(e, a) e.a = a end) -local test_comp_3 = Concord.component("test_comp_3", function(e, b) +Component("test_comp_3", function(e, b) e.b = b end) -local test_system = Concord.system({Component.test_comp_1}) +local test_system = System("test_system", {Components.test_comp_1}) -function onEntityAdded(e) +local function onEntityAdded(e) -- luacheck: ignore print("Added") end -function onEntityRemoved(e) +local function onEntityRemoved(e) -- luacheck: ignore print("Removed") end function test_system:init() - self.pool.onEntityAdded = onEntityAdded + self.pool.onEntityAdded = onEntityAdded self.pool.onEntityRemoved = onEntityRemoved end -function test_system:update(dt) +function test_system:update(dt) -- luacheck: ignore + --print(#self.pool) +end + +function test_system:update2(dt) -- luacheck: ignore --print(#self.pool) end +local world = World() -local world = Concord.world() - -local entity = Concord.entity() -entity:give(Component.test_comp_1, 100, 100) +local entity = Entity() +entity:give(Components.test_comp_1, 100, 100) world:addEntity(entity) -world:addSystem(test_system(), "update") +world:addSystem(Systems.test_system, "update") +world:addSystem(Systems.test_system, "update", "update2") function love.update(dt) world:flush() @@ -59,10 +72,10 @@ end function love.keypressed(key) if key == "q" then - entity:remove(Component.test_comp_1) + entity:remove(Components.test_comp_1) end if key == "w" then - entity:give(Component.test_comp_1) + entity:give(Components.test_comp_1) end if key == "e" then world:removeEntity(entity) diff --git a/src/component.lua b/src/component.lua index 102e722..43d4ecc 100644 --- a/src/component.lua +++ b/src/component.lua @@ -1,5 +1,9 @@ --- Component +local PATH = (...):gsub('%.[^%.]+$', '') + +local Components = require(PATH..".components") + local Component = {} Component.__index = Component @@ -15,10 +19,6 @@ function Component.new(name, populate) error("bad argument #2 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) end - if (Component[name] ~= nil) then - error("bad argument #2 to 'Component.new' (Component with name '"..name.."' already exists", 2) - end - local component = setmetatable({ __name = name, __populate = populate, @@ -28,7 +28,7 @@ function Component.new(name, populate) component.__mt = {__index = component} - Component[name] = component + Components.register(name, component) return component end @@ -51,4 +51,4 @@ return setmetatable(Component, { __call = function(_, ...) return Component.new(...) end, -}) \ No newline at end of file +}) \ No newline at end of file diff --git a/src/components.lua b/src/components.lua new file mode 100644 index 0000000..63d40e0 --- /dev/null +++ b/src/components.lua @@ -0,0 +1,29 @@ +-- Components + +local PATH = (...):gsub('%.[^%.]+$', '') + +local Type = require(PATH..".type") + +local Components = {} + +function Components.register(name, component) + if (type(name) ~= "string") then + error("bad argument #1 to 'Components.register' (string expected, got "..type(name)..")", 3) + end + + if (not Type.isComponent(component)) then + error("bad argument #2 to 'Components.register' (component expected, got "..type(component)..")", 3) + end + + if (rawget(Components, name)) then + error("bad argument #2 to 'Components.register' (Component with name '"..name.."' is already registerd)", 3) + end + + Components[name] = component +end + +return setmetatable(Components, { + __index = function(_, name) + error("Attempt to index component '"..tostring(name).."' that does not exist / was not registered", 2) + end +}) \ No newline at end of file diff --git a/src/init.lua b/src/init.lua index e13f190..4eb9b99 100644 --- a/src/init.lua +++ b/src/init.lua @@ -33,7 +33,9 @@ local Concord = { Concord.entity = require(PATH..".entity") 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.assemblage = require(PATH..".assemblage") diff --git a/src/list.lua b/src/list.lua index 16819da..b3e8fdf 100644 --- a/src/list.lua +++ b/src/list.lua @@ -60,6 +60,8 @@ function List:remove(obj) self[obj] = nil self.size = size - 1 + + return self end --- Gets an object by numerical index. diff --git a/src/pool.lua b/src/pool.lua index e82735a..92373e2 100644 --- a/src/pool.lua +++ b/src/pool.lua @@ -45,10 +45,10 @@ function Pool:remove(e) self:onEntityRemoved(e) end -function Pool:onEntityAdded(e) +function Pool:onEntityAdded(e) -- luacheck: ignore end -function Pool:onEntityRemoved(e) +function Pool:onEntityRemoved(e) -- luacheck: ignore end return setmetatable(Pool, { diff --git a/src/system.lua b/src/system.lua index b078a50..ecafd6b 100644 --- a/src/system.lua +++ b/src/system.lua @@ -2,20 +2,21 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Pool = require(PATH..".pool") +local Systems = require(PATH..".systems") +local Pool = require(PATH..".pool") local System = {} System.mt = { __index = System, - __call = function(systemProto, ...) + __call = function(baseSystem, world) local system = setmetatable({ __pools = {}, - __world = nil, + __world = world, __isSystem = true, - }, systemProto) + }, baseSystem) - for _, filter in pairs(systemProto.__filter) do + for _, filter in pairs(baseSystem.__filter) do local pool = system:__buildPool(filter) if not system[pool.name] then system[pool.name] = pool @@ -25,7 +26,8 @@ System.mt = { end end - system:init(...) + system:init(world) + return system end, } @@ -33,13 +35,21 @@ System.mt = { --- Creates a new System prototype. -- @param ... Variable amounts of filters -- @return A new System prototype -function System.new(...) - local systemProto = setmetatable({ +function System.new(name, ...) + if (type(name) ~= "string") then + error("bad argument #1 to 'System.new' (string expected, got "..type(name)..")", 2) + end + + local baseSystem = setmetatable({ + __name = name, + __isBaseSystem = true, __filter = {...}, }, System.mt) - systemProto.__index = systemProto + baseSystem.__index = baseSystem - return systemProto + Systems.register(name, baseSystem) + + return baseSystem end --- Builds a Pool for the System. @@ -98,13 +108,8 @@ function System:getWorld() end --- Default callback for system initialization. --- @param ... Varags -function System:init(...) -- luacheck: ignore -end - --- Default callback for when the System is added to an World. -- @param world The World the System was added to -function System:addedTo(world) -- luacheck: ignore +function System:init(world) -- luacheck: ignore end -- Default callback for when a System's callback is enabled. diff --git a/src/systems.lua b/src/systems.lua new file mode 100644 index 0000000..4f84472 --- /dev/null +++ b/src/systems.lua @@ -0,0 +1,29 @@ +-- Systems + +local PATH = (...):gsub('%.[^%.]+$', '') + +local Type = require(PATH..".type") + +local Systems = {} + +function Systems.register(name, system) + if (type(name) ~= "string") then + error("bad argument #1 to 'Systems.register' (string expected, got "..type(name)..")", 3) + end + + if (not Type.isBaseSystem(system)) then + error("bad argument #2 to 'Systems.register' (baseSystem expected, got "..type(system)..")", 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] = system +end + +return setmetatable(Systems, { + __index = function(_, name) + error("Attempt to index system '"..tostring(name).."' that does not exist / was not registered", 2) + end +}) \ No newline at end of file diff --git a/src/type.lua b/src/type.lua index 999c42c..ae79628 100644 --- a/src/type.lua +++ b/src/type.lua @@ -3,23 +3,23 @@ local Type = {} function Type.isComponent(t) - return type(t) == "table" and t.__isComponent + return type(t) == "table" and t.__isComponent or false end function Type.isEntity(t) - return type(t) == "table" and t.__isEntity + return type(t) == "table" and t.__isEntity or false +end + +function Type.isBaseSystem(t) + return type(t) == "table" and t.__isBaseSystem or false end function Type.isSystem(t) - return type(t) == "table" and t.__isSystem -end - -function Type.isContext(t) - return type(t) == "table" and t.__isContext + return type(t) == "table" and t.__isSystem or false end function Type.isAssemblage(t) - return type(t) == "table" and t.__isAssemblage + return type(t) == "table" and t.__isAssemblage or false end return Type diff --git a/src/world.lua b/src/world.lua index 2712cb6..2bb13e5 100644 --- a/src/world.lua +++ b/src/world.lua @@ -14,11 +14,14 @@ function World.new() local world = setmetatable({ entities = List(), systems = List(), + events = {}, __added = {}, __removed = {}, + __systemLookup = {}, + __isWorld = true, }, World) @@ -68,8 +71,8 @@ function World:flush() e.__wasAdded = false e.__isDirty = false - for i = 1, self.systems.size do - self.systems:get(i):__evaluate(e) + for j = 1, self.systems.size do + self.systems:get(j):__evaluate(e) end self:onEntityAdded(e) @@ -79,16 +82,16 @@ function World:flush() e.world = nil self.entities:remove(e) - for i = 1, self.systems.size do - self.systems:get(i):__remove(e) + for j = 1, self.systems.size do + self.systems:get(j):__remove(e) end e.__wasRemoved = false end if e.__isDirty then - for i = 1, self.systems.size do - self.systems:get(i):__evaluate(e) + for j = 1, self.systems.size do + self.systems:get(j):__evaluate(e) end e.__isDirty = false @@ -99,43 +102,46 @@ function World:flush() end --- Adds a System to the World. --- @param system The System to add --- @param eventName The Event to register to --- @param callback The function name to call. Defaults to eventName +-- @param baseSystem The BaseSystem of the system to add +-- @param callbackName The callbackName to register to +-- @param callback The function name to call. Defaults to callbackName -- @param enabled If the system is enabled. Defaults to true -- @return self -function World:addSystem(system, eventName, callback, enabled) - if not Type.isSystem(system) then - error("bad argument #1 to 'World:addSystem' (System expected, got "..type(system)..")", 2) +function World:addSystem(baseSystem, callbackName, callback, enabled) + if not Type.isBaseSystem(baseSystem) then + error("bad argument #1 to 'World:addSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) end - if system.__World and system.__World ~= self then - error("System already in World '" ..tostring(system.__World).."'") - end + local system = self.__systemLookup[baseSystem] + if (not system) then + -- System was not created for this world yet, so we create it ourselves + + print("Created system") + + system = baseSystem(self) + + self.__systemLookup[baseSystem] = system - if not self.systems:has(system) then self.systems:add(system) - system.__World = self - - system:addedTo(self) - - for i = 1, self.entities.size do - system:__evaluate(self.entities:get(i)) - end end - if eventName then - self.events[eventName] = self.events[eventName] or {} + -- Retroactively evaluate all entities for this system + for i = 1, self.entities.size do + system:__evaluate(self.entities:get(i)) + end - local i = #self.events[eventName] + 1 - self.events[eventName][i] = { + if callbackName then + self.events[callbackName] = self.events[callbackName] or {} + + local i = #self.events[callbackName] + 1 + self.events[callbackName][i] = { system = system, - callback = callback or eventName, + callback = callback or callbackName, enabled = enabled == nil or enabled, } if enabled == nil or enabled then - system:enabledCallback(callback or eventName) + system:enabledCallback(callback or callbackName) end end @@ -144,15 +150,15 @@ end --- Enables a System in the World. -- @param system The System to enable --- @param eventName The Event it was registered to +-- @param callbackName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function World:enableSystem(system, eventName, callback) +function World:enableSystem(system, callbackName, callback) if not Type.isSystem(system) then error("bad argument #1 to 'World:enableSystem' (System expected, got "..type(system)..")", 2) end - return self:setSystem(system, eventName, callback, true) + return self:setSystem(system, callbackName, callback, true) end --- Disables a System in the World. @@ -160,12 +166,12 @@ end -- @param eventName The Event it was registered to -- @param callback The callback it was registered with. Defaults to eventName -- @return self -function World:disableSystem(system, eventName, callback) +function World:disableSystem(system, callbackName, callback) if not Type.isSystem(system) then error("bad argument #1 to 'World:disableSystem' (System expected, got "..type(system)..")", 2) end - return self:setSystem(system, eventName, callback, false) + return self:setSystem(system, callbackName, callback, false) end --- Sets a System 'enable' in the World. @@ -174,15 +180,15 @@ end -- @param callback The callback it was registered with. Defaults to eventName -- @param enable The state to set it to -- @return self -function World:setSystem(system, eventName, callback, enable) +function World:setSystem(system, callbackName, callback, enable) if not Type.isSystem(system) then error("bad argument #1 to 'World:setSystem' (System expected, got "..type(system)..")", 2) end - callback = callback or eventName + callback = callback or callbackName if callback then - local listeners = self.events[eventName] + local listeners = self.events[callbackName] if listeners then for i = 1, #listeners do @@ -210,12 +216,12 @@ end -- @param eventName The Event that should be emitted -- @param ... Parameters passed to listeners -- @return self -function World:emit(eventName, ...) - if not eventName or type(eventName) ~= "string" then - error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")") +function World:emit(callbackName, ...) + if not callbackName or type(callbackName) ~= "string" then + error("bad argument #1 to 'World:emit' (String expected, got "..type(callbackName)..")") end - local listeners = self.events[eventName] + local listeners = self.events[callbackName] if listeners then for i = 1, #listeners do @@ -234,11 +240,9 @@ end -- @return self function World:clear() for i = 1, self.entities.size do - self.entities:get(i):destroy() + self.removeEntity(self.entities:get(i)) end - self:flush() - return self end From d0e227485e181d6ab9948af643828dcd663c5c7a Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Thu, 19 Dec 2019 20:14:21 +0100 Subject: [PATCH 015/113] Allow named worlds --- main.lua | 6 +++++- src/component.lua | 2 +- src/system.lua | 1 + src/type.lua | 4 ++++ src/world.lua | 15 ++++++++++----- src/worlds.lua | 29 +++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 src/worlds.lua diff --git a/main.lua b/main.lua index 26ef1f8..6040302 100644 --- a/main.lua +++ b/main.lua @@ -46,7 +46,11 @@ function test_system:init() end function test_system:update(dt) -- luacheck: ignore - --print(#self.pool) + --[=[ + for _, v in ipairs(self.pool) do + print(v) + end + ]=] end function test_system:update2(dt) -- luacheck: ignore diff --git a/src/component.lua b/src/component.lua index 43d4ecc..e7b3d90 100644 --- a/src/component.lua +++ b/src/component.lua @@ -38,7 +38,7 @@ end -- @return A new initialized Bag function Component:__initialize(...) if self.__populate then - local bag = setmetatable({}, self.__mt) + local bag = setmetatable({}, self) self.__populate(bag, ...) return bag diff --git a/src/system.lua b/src/system.lua index ecafd6b..f9b696c 100644 --- a/src/system.lua +++ b/src/system.lua @@ -14,6 +14,7 @@ System.mt = { __world = world, __isSystem = true, + __isBaseSystem = false, -- Overwrite value from baseSystem }, baseSystem) for _, filter in pairs(baseSystem.__filter) do diff --git a/src/type.lua b/src/type.lua index ae79628..3da59d9 100644 --- a/src/type.lua +++ b/src/type.lua @@ -18,6 +18,10 @@ function Type.isSystem(t) return type(t) == "table" and t.__isSystem or false end +function Type.isWorld(t) + return type(t) == "table" and t.__isWorld or false +end + function Type.isAssemblage(t) return type(t) == "table" and t.__isAssemblage or false end diff --git a/src/world.lua b/src/world.lua index 2bb13e5..86e76c3 100644 --- a/src/world.lua +++ b/src/world.lua @@ -2,6 +2,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') +local Worlds = require(PATH..".world") local Type = require(PATH..".type") local List = require(PATH..".list") @@ -10,7 +11,11 @@ World.__index = World --- Creates a new World. -- @return The new World -function World.new() +function World.new(name) + if (type(name) ~= "string") then + error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) + end + local world = setmetatable({ entities = List(), systems = List(), @@ -22,9 +27,12 @@ function World.new() __systemLookup = {}, + __name = name, __isWorld = true, }, World) + Worlds.register(world) + return world end @@ -115,9 +123,6 @@ function World:addSystem(baseSystem, callbackName, callback, enabled) local system = self.__systemLookup[baseSystem] if (not system) then -- System was not created for this world yet, so we create it ourselves - - print("Created system") - system = baseSystem(self) self.__systemLookup[baseSystem] = system @@ -240,7 +245,7 @@ end -- @return self function World:clear() for i = 1, self.entities.size do - self.removeEntity(self.entities:get(i)) + self.removeEntity(self.entities[i]) end return self diff --git a/src/worlds.lua b/src/worlds.lua new file mode 100644 index 0000000..0f8ad9c --- /dev/null +++ b/src/worlds.lua @@ -0,0 +1,29 @@ +-- Worlds + +local PATH = (...):gsub('%.[^%.]+$', '') + +local Type = require(PATH..".type") + +local Worlds = {} + +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] = component +end + +return setmetatable(Worlds, { + __index = function(_, name) + error("Attempt to index world '"..tostring(name).."' that does not exist / was not registered", 2) + end +}) \ No newline at end of file From 276a053b7fbe50662c1408475d91304ae011dffb Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Thu, 19 Dec 2019 20:19:02 +0100 Subject: [PATCH 016/113] Allow named assemblages --- main.lua | 7 ++++--- src/assemblage.lua | 13 ++++++++++++- src/assemblages.lua | 29 +++++++++++++++++++++++++++++ src/init.lua | 8 +++++++- src/world.lua | 6 +++--- src/worlds.lua | 2 +- 6 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 src/assemblages.lua diff --git a/main.lua b/main.lua index 6040302..68bad43 100644 --- a/main.lua +++ b/main.lua @@ -15,7 +15,8 @@ local Systems = Concord.systems local Entity = Concord.entity -local World = Concord.world +local World = Concord.world +local Worlds = Concord.worlds Component("test_comp_1", function(e, x, y) e.x = x @@ -58,12 +59,12 @@ function test_system:update2(dt) -- luacheck: ignore end -local world = World() +local world = World("testWorld") local entity = Entity() entity:give(Components.test_comp_1, 100, 100) -world:addEntity(entity) +Worlds.testWorld:addEntity(entity) world:addSystem(Systems.test_system, "update") world:addSystem(Systems.test_system, "update", "update2") diff --git a/src/assemblage.lua b/src/assemblage.lua index 79b3cbb..a6befed 100644 --- a/src/assemblage.lua +++ b/src/assemblage.lua @@ -1,14 +1,25 @@ --- Assemblage +local PATH = (...):gsub('%.[^%.]+$', '') + +local Assemblages = require(PATH..".world") + local Assemblage = {} Assemblage.__index = Assemblage -function Assemblage.new(assemble) +function Assemblage.new(name, assemble) + if (type(name) ~= "string") then + error("bad argument #1 to 'Assemblage.new' (string expected, got "..type(name)..")", 2) + end + local assemblage = setmetatable({ __assemble = assemble, + __name = name, __isAssemblage = true, }, Assemblage) + + Assemblages.register(name, assemblage) return assemblage end diff --git a/src/assemblages.lua b/src/assemblages.lua new file mode 100644 index 0000000..f86f629 --- /dev/null +++ b/src/assemblages.lua @@ -0,0 +1,29 @@ +-- Assemblages + +local PATH = (...):gsub('%.[^%.]+$', '') + +local Type = require(PATH..".type") + +local Assemblages = {} + +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(world)..")", 3) + end + + if (rawget(Assemblages, name)) then + error("bad argument #2 to 'Assemblages.register' (Assemblage with name '"..name.."' is already registerd)", 3) + end + + Assemblages[name] = assemblage +end + +return setmetatable(Assemblages, { + __index = function(_, name) + error("Attempt to index assemblage '"..tostring(name).."' that does not exist / was not registered", 2) + end +}) \ No newline at end of file diff --git a/src/init.lua b/src/init.lua index 4eb9b99..5f32550 100644 --- a/src/init.lua +++ b/src/init.lua @@ -32,11 +32,17 @@ local Concord = { } Concord.entity = require(PATH..".entity") + 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.assemblage = require(PATH..".assemblage") +Concord.worlds = require(PATH..".worlds") + +Concord.assemblage = require(PATH..".assemblage") +Concord.assemblages = require(PATH..".assemblages") return Concord diff --git a/src/world.lua b/src/world.lua index 86e76c3..217084e 100644 --- a/src/world.lua +++ b/src/world.lua @@ -2,7 +2,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Worlds = require(PATH..".world") +local Worlds = require(PATH..".worlds") local Type = require(PATH..".type") local List = require(PATH..".list") @@ -13,7 +13,7 @@ World.__index = World -- @return The new World function World.new(name) if (type(name) ~= "string") then - error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) + error("bad argument #1 to 'World.new' (string expected, got "..type(name)..")", 2) end local world = setmetatable({ @@ -31,7 +31,7 @@ function World.new(name) __isWorld = true, }, World) - Worlds.register(world) + Worlds.register(name, world) return world end diff --git a/src/worlds.lua b/src/worlds.lua index 0f8ad9c..2f8f5a8 100644 --- a/src/worlds.lua +++ b/src/worlds.lua @@ -19,7 +19,7 @@ function Worlds.register(name, world) error("bad argument #2 to 'Worlds.register' (World with name '"..name.."' is already registerd)", 3) end - Worlds[name] = component + Worlds[name] = world end return setmetatable(Worlds, { From 2057767e752b7f5d2012244210ae14d8ec2e051c Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 20 Dec 2019 19:00:47 +0100 Subject: [PATCH 017/113] Add world:hasSystem, world:getSystem --- src/world.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/world.lua b/src/world.lua index 217084e..50377d7 100644 --- a/src/world.lua +++ b/src/world.lua @@ -109,6 +109,22 @@ function World:flush() return self end +function World:hasSystem(baseSystem) + if not Type.isBaseSystem(baseSystem) then + error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) + end + + return self.__systemLookup[baseSystem] and true or false +end + +function World:getSystem(baseSystem) + if not Type.isBaseSystem(baseSystem) then + error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) + end + + return self.__systemLookup[baseSystem] +end + --- Adds a System to the World. -- @param baseSystem The BaseSystem of the system to add -- @param callbackName The callbackName to register to From 073aadf74b8a044c59a6914444134b79f220c805 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 20 Dec 2019 19:25:11 +0100 Subject: [PATCH 018/113] Test commit --- main.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.lua b/main.lua index 68bad43..392a4f0 100644 --- a/main.lua +++ b/main.lua @@ -5,6 +5,8 @@ local file = "examples.simpleDrawing" require(file) ]=]-- +--test + local Concord = require("src") local Component = require("src.component") From bb508ee94762ccad7f2b7260f2e3bc795d83ee7a Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 21 Dec 2019 12:00:48 +0100 Subject: [PATCH 019/113] Add Entity:getComponents --- src/entity.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/entity.lua b/src/entity.lua index a6c6b29..14619cf 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -13,6 +13,8 @@ function Entity.new() local e = setmetatable({ world = nil, + __components = {}, + __isDirty = true, __wasAdded = false, __wasRemoved = false, @@ -23,15 +25,18 @@ function Entity.new() return e end -local function give(e, component, ...) - local comp = component:__initialize(...) - e[component] = comp +local function give(e, baseComponent, ...) + local component = baseComponent:__initialize(...) + + e[baseComponent] = component + e.__components[baseComponent] = component e.__isDirty = true end -local function remove(e, component) - e[component] = nil +local function remove(e, baseComponent) + e[baseComponent] = nil + e.__components[baseComponent] = nil e.__isDirty = true end @@ -119,6 +124,10 @@ function Entity:has(component) return self[component] ~= nil end +function Entity:getComponents() + return self.__components +end + return setmetatable(Entity, { __call = function(_, ...) return Entity.new(...) From ecb3c2db7e3f911f16924be26e94ff08ce596381 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 21:00:53 +0100 Subject: [PATCH 020/113] Cache added and removed components --- main.lua | 13 +++++++++---- src/component.lua | 5 ++++- src/entity.lua | 46 +++++++++++++++++++++++++++++++++++++++++----- src/world.lua | 21 +++++++++++++-------- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/main.lua b/main.lua index 392a4f0..6b9f293 100644 --- a/main.lua +++ b/main.lua @@ -35,11 +35,12 @@ end) local test_system = System("test_system", {Components.test_comp_1}) -local function onEntityAdded(e) -- luacheck: ignore - print("Added") +local function onEntityAdded(pool, e) -- luacheck: ignore + local test_comp = e:get(Components.test_comp_1) + print(test_comp.x) end -local function onEntityRemoved(e) -- luacheck: ignore +local function onEntityRemoved(pool, e) -- luacheck: ignore print("Removed") end @@ -64,7 +65,11 @@ end local world = World("testWorld") local entity = Entity() -entity:give(Components.test_comp_1, 100, 100) +entity +:give(Components.test_comp_1, 100, 100) +:remove(Components.test_comp_1) +:give(Components.test_comp_1, 200, 100) + Worlds.testWorld:addEntity(entity) diff --git a/src/component.lua b/src/component.lua index e7b3d90..e1ef5aa 100644 --- a/src/component.lua +++ b/src/component.lua @@ -38,7 +38,10 @@ end -- @return A new initialized Bag function Component:__initialize(...) if self.__populate then - local bag = setmetatable({}, self) + local bag = setmetatable({ + __baseComponent = self, + }, self) + self.__populate(bag, ...) return bag diff --git a/src/entity.lua b/src/entity.lua index 14619cf..5bfc88e 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -13,9 +13,13 @@ function Entity.new() local e = setmetatable({ world = nil, + __addedComponents = {}, + __removedComponents = {}, + __operations = {}, + __components = {}, - __isDirty = true, + __isDirty = false, __wasAdded = false, __wasRemoved = false, @@ -25,18 +29,30 @@ function Entity.new() return e end -local function give(e, baseComponent, ...) - local component = baseComponent:__initialize(...) +local function giveOperation(e, component) + local baseComponent = component.__baseComponent e[baseComponent] = component e.__components[baseComponent] = component +end + +local function removeOperation(e, baseComponent) + e[baseComponent] = nil + e.__components[baseComponent] = nil +end + +local function give(e, baseComponent, ...) + local component = baseComponent:__initialize(...) + + e.__addedComponents[#e.__addedComponents + 1] = component + e.__operations[#e.__operations + 1] = giveOperation e.__isDirty = true end local function remove(e, baseComponent) - e[baseComponent] = nil - e.__components[baseComponent] = nil + e.__removedComponents[#e.__removedComponents + 1] = baseComponent + e.__operations[#e.__operations + 1] = removeOperation e.__isDirty = true end @@ -92,6 +108,26 @@ function Entity:assemble(assemblage, ...) return self end +function Entity:flush() + local addi, removei = 1, 1 + + for i = 1, #self.__operations do + local operation = self.__operations[i] + + if (operation == giveOperation) then + operation(self, self.__addedComponents[addi]) + self.__addedComponents[addi] = nil + addi = addi + 1 + elseif (operation == removeOperation) then + operation(self, self.__removedComponents[removei]) + self.__removedComponents[removei] = nil + removei = removei + 1 + end + + self.__operations[i] = nil + end +end + --- Destroys the Entity. -- @return self function Entity:destroy() diff --git a/src/world.lua b/src/world.lua index 50377d7..8a47aea 100644 --- a/src/world.lua +++ b/src/world.lua @@ -75,9 +75,20 @@ function World:flush() for i = 1, self.entities.size do e = self.entities:get(i) + if e.__isDirty then + e:flush() + + if (not e.__wasAdded) then -- The __wasAdded check below will handle this instead + for j = 1, self.systems.size do + self.systems:get(j):__evaluate(e) + end + end + + e.__isDirty = false + end + if e.__wasAdded then e.__wasAdded = false - e.__isDirty = false for j = 1, self.systems.size do self.systems:get(j):__evaluate(e) @@ -95,14 +106,8 @@ function World:flush() end e.__wasRemoved = false - end - if e.__isDirty then - for j = 1, self.systems.size do - self.systems:get(j):__evaluate(e) - end - - e.__isDirty = false + self:onEntityRemoved(e) end end From 89443bd9619faaa9b1b5348161445ed23841624d Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 21:01:47 +0100 Subject: [PATCH 021/113] Make e:flush 'private' --- src/entity.lua | 2 +- src/world.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity.lua b/src/entity.lua index 5bfc88e..a3d6483 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -108,7 +108,7 @@ function Entity:assemble(assemblage, ...) return self end -function Entity:flush() +function Entity:__flush() local addi, removei = 1, 1 for i = 1, #self.__operations do diff --git a/src/world.lua b/src/world.lua index 8a47aea..63ef1a9 100644 --- a/src/world.lua +++ b/src/world.lua @@ -76,7 +76,7 @@ function World:flush() e = self.entities:get(i) if e.__isDirty then - e:flush() + e:__flush() if (not e.__wasAdded) then -- The __wasAdded check below will handle this instead for j = 1, self.systems.size do From f6536d5a0ee42c4b460fc78b1ac1da2f4e7dec4a Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 21:02:25 +0100 Subject: [PATCH 022/113] Add entity:getWorld --- src/entity.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/entity.lua b/src/entity.lua index a3d6483..25b063e 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -11,7 +11,7 @@ Entity.__index = Entity -- @return A new Entity function Entity.new() local e = setmetatable({ - world = nil, + __world = nil, __addedComponents = {}, __removedComponents = {}, @@ -164,6 +164,10 @@ function Entity:getComponents() return self.__components end +function Entity:getWorld() + return self.__world +end + return setmetatable(Entity, { __call = function(_, ...) return Entity.new(...) From e0f88025bace537ee0d588427c270703849df61b Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 21:08:30 +0100 Subject: [PATCH 023/113] Clean up component vs baseComponent --- main.lua | 3 +-- src/component.lua | 33 ++++++++++++++++----------------- src/components.lua | 12 ++++++------ src/entity.lua | 42 +++++++++++++++++++++--------------------- src/type.lua | 12 ++++++++---- 5 files changed, 52 insertions(+), 50 deletions(-) diff --git a/main.lua b/main.lua index 6b9f293..f155a83 100644 --- a/main.lua +++ b/main.lua @@ -36,8 +36,7 @@ end) local test_system = System("test_system", {Components.test_comp_1}) local function onEntityAdded(pool, e) -- luacheck: ignore - local test_comp = e:get(Components.test_comp_1) - print(test_comp.x) + print("Added") end local function onEntityRemoved(pool, e) -- luacheck: ignore diff --git a/src/component.lua b/src/component.lua index e1ef5aa..fd532ea 100644 --- a/src/component.lua +++ b/src/component.lua @@ -15,39 +15,38 @@ function Component.new(name, populate) error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) end - if not (populate == nil or type(populate) == "function") then - error("bad argument #2 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) + if not (type(populate) == "function") then + error("bad argument #2 to 'Component.new' (function expected, got "..type(populate)..")", 2) end - local component = setmetatable({ + local baseComponent = setmetatable({ __name = name, __populate = populate, - __isComponent = true, + __isBaseComponent = true, }, Component) - component.__mt = {__index = component} + baseComponent.__mt = {__index = baseComponent} - Components.register(name, component) + Components.register(name, baseComponent) - return component + return baseComponent end ---- Creates and initializes a new Bag. +--- Creates and initializes a new Component. -- @param ... The values passed to the populate function --- @return A new initialized Bag +-- @return A new initialized Component function Component:__initialize(...) - if self.__populate then - local bag = setmetatable({ - __baseComponent = self, - }, self) + local component = setmetatable({ + __baseComponent = self, - self.__populate(bag, ...) + __isComponent = true, + __isBaseComponent = false, + }, self) - return bag - end + self.__populate(component, ...) - return true + return component end return setmetatable(Component, { diff --git a/src/components.lua b/src/components.lua index 63d40e0..2249774 100644 --- a/src/components.lua +++ b/src/components.lua @@ -6,24 +6,24 @@ local Type = require(PATH..".type") local Components = {} -function Components.register(name, component) +function Components.register(name, baseComponent) if (type(name) ~= "string") then error("bad argument #1 to 'Components.register' (string expected, got "..type(name)..")", 3) end - if (not Type.isComponent(component)) then - error("bad argument #2 to 'Components.register' (component expected, got "..type(component)..")", 3) + if (not Type.isBaseComponent(baseComponent)) then + error("bad argument #2 to 'Components.register' (BaseComponent expected, got "..type(baseComponent)..")", 3) end if (rawget(Components, name)) then - error("bad argument #2 to 'Components.register' (Component with name '"..name.."' is already registerd)", 3) + error("bad argument #2 to 'Components.register' (BaseComponent with name '"..name.."' is already registerd)", 3) end - Components[name] = component + Components[name] = baseComponent end return setmetatable(Components, { __index = function(_, name) - error("Attempt to index component '"..tostring(name).."' that does not exist / was not registered", 2) + error("Attempt to index BaseComponent '"..tostring(name).."' that does not exist / was not registered", 2) end }) \ No newline at end of file diff --git a/src/entity.lua b/src/entity.lua index 25b063e..98d1c97 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -61,26 +61,26 @@ end -- @param component The Component to add -- @param ... The values passed to the Component -- @return self -function Entity:give(component, ...) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2) +function Entity:give(baseComponent, ...) + if not Type.isBaseComponent(baseComponent) then + error("bad argument #1 to 'Entity:give' (BaseComponent expected, got "..type(baseComponent)..")", 2) end - give(self, component, ...) + give(self, baseComponent, ...) return self end -function Entity:ensure(component, ...) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:ensure' (Component expected, got "..type(component)..")", 2) +function Entity:ensure(baseComponent, ...) + if not Type.isBaseComponent(baseComponent) then + error("bad argument #1 to 'Entity:ensure' (BaseComponent expected, got "..type(baseComponent)..")", 2) end - if self[component] then + if self[baseComponent] then return self end - give(self, component, ...) + give(self, baseComponent, ...) return self end @@ -88,12 +88,12 @@ end --- Removes a component from an Entity. -- @param component The Component to remove -- @return self -function Entity:remove(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")") +function Entity:remove(baseComponent) + if not Type.isBaseComponent(baseComponent) then + error("bad argument #1 to 'Entity:remove' (BaseComponent expected, got "..type(baseComponent)..")") end - remove(self, component) + remove(self, baseComponent) return self end @@ -141,23 +141,23 @@ end --- Gets a Component from the Entity. -- @param component The Component to get -- @return The Bag from the Component -function Entity:get(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:get' (Component expected, got "..type(component)..")") +function Entity:get(baseComponent) + if not Type.isBaseComponent(baseComponent) then + error("bad argument #1 to 'Entity:get' (BaseComponent expected, got "..type(baseComponent)..")") end - return self[component] + return self[baseComponent] end --- Returns true if the Entity has the Component. -- @param component The Component to check against -- @return True if the entity has the Bag. False otherwise -function Entity:has(component) - if not Type.isComponent(component) then - error("bad argument #1 to 'Entity:has' (Component expected, got "..type(component)..")") +function Entity:has(baseComponent) + if not Type.isBaseComponent(baseComponent) then + error("bad argument #1 to 'Entity:has' (BaseComponent expected, got "..type(baseComponent)..")") end - return self[component] ~= nil + return self[baseComponent] ~= nil end function Entity:getComponents() diff --git a/src/type.lua b/src/type.lua index 3da59d9..a7ba264 100644 --- a/src/type.lua +++ b/src/type.lua @@ -2,14 +2,18 @@ local Type = {} -function Type.isComponent(t) - return type(t) == "table" and t.__isComponent or false -end - function Type.isEntity(t) return type(t) == "table" and t.__isEntity or false end +function Type.isBaseComponent(t) + return type(t) == "table" and t.__isBaseComponent or false +end + +function Type.isComponent(t) + return type(t) == "table" and t.__isComponent or false +end + function Type.isBaseSystem(t) return type(t) == "table" and t.__isBaseSystem or false end From 144a42dc9ed0f9bb6ea1085bee9d30df3c119df2 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 23:07:42 +0100 Subject: [PATCH 024/113] Automate naming process --- main.lua | 24 ++++++++------- src/assemblage.lua | 13 +------- src/component.lua | 15 ++-------- src/init.lua | 53 +++++++++++++++++++++++++++++++++ src/system.lua | 10 +------ src/world.lua | 10 +------ test/components/test_comp_1.lua | 6 ++++ 7 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 test/components/test_comp_1.lua diff --git a/main.lua b/main.lua index f155a83..1a5ed40 100644 --- a/main.lua +++ b/main.lua @@ -9,8 +9,8 @@ require(file) local Concord = require("src") -local Component = require("src.component") -local Components = require("src.components") +local Component = Concord.component +local Components = Concord.components local System = Concord.system local Systems = Concord.systems @@ -20,20 +20,23 @@ local Entity = Concord.entity local World = Concord.world local Worlds = Concord.worlds -Component("test_comp_1", function(e, x, y) - e.x = x - e.y = y -end) +Concord.loadComponents("test/components") -Component("test_comp_2", function(e, a) + +local test_comp_2 = Component(function(e, a) e.a = a end) +Components.register("test_comp_2", test_comp_2) -Component("test_comp_3", function(e, b) + +local test_comp_3 = Component(function(e, b) e.b = b end) +Components.register("test_comp_3", test_comp_3) -local test_system = System("test_system", {Components.test_comp_1}) + +local test_system = System({Components.test_comp_1}) +Systems.register("test_system", test_system) local function onEntityAdded(pool, e) -- luacheck: ignore print("Added") @@ -61,7 +64,8 @@ function test_system:update2(dt) -- luacheck: ignore end -local world = World("testWorld") +local world = World() +Worlds.register("testWorld", world) local entity = Entity() entity diff --git a/src/assemblage.lua b/src/assemblage.lua index a6befed..79b3cbb 100644 --- a/src/assemblage.lua +++ b/src/assemblage.lua @@ -1,25 +1,14 @@ --- Assemblage -local PATH = (...):gsub('%.[^%.]+$', '') - -local Assemblages = require(PATH..".world") - local Assemblage = {} Assemblage.__index = Assemblage -function Assemblage.new(name, assemble) - if (type(name) ~= "string") then - error("bad argument #1 to 'Assemblage.new' (string expected, got "..type(name)..")", 2) - end - +function Assemblage.new(assemble) local assemblage = setmetatable({ __assemble = assemble, - __name = name, __isAssemblage = true, }, Assemblage) - - Assemblages.register(name, assemblage) return assemblage end diff --git a/src/component.lua b/src/component.lua index fd532ea..90dcdc6 100644 --- a/src/component.lua +++ b/src/component.lua @@ -1,26 +1,17 @@ --- Component -local PATH = (...):gsub('%.[^%.]+$', '') - -local Components = require(PATH..".components") - local Component = {} Component.__index = Component --- Creates a new Component. -- @param populate A function that populates the Bag with values -- @return A Component object -function Component.new(name, populate) - if (type(name) ~= "string") then - error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) - end - +function Component.new(populate) if not (type(populate) == "function") then - error("bad argument #2 to 'Component.new' (function expected, got "..type(populate)..")", 2) + error("bad argument #1 to 'Component.new' (function expected, got "..type(populate)..")", 2) end local baseComponent = setmetatable({ - __name = name, __populate = populate, __isBaseComponent = true, @@ -28,8 +19,6 @@ function Component.new(name, populate) baseComponent.__mt = {__index = baseComponent} - Components.register(name, baseComponent) - return baseComponent end diff --git a/src/init.lua b/src/init.lua index 5f32550..66916db 100644 --- a/src/init.lua +++ b/src/init.lua @@ -45,4 +45,57 @@ 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 + error("bad argument #1 to 'load' (string/table of strings expected, got "..type(pathOrFiles)..")", 3) -- luacheck: ignore + end + + if (type(pathOrFiles) == "string") then + local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore + if (info == nil or info.type ~= "directory") then + error("bad argument #1 to 'load' (path '"..pathOrFiles.."' not found)", 3) -- luacheck: ignore + end + + local files = love.filesystem.getDirectoryItems(pathOrFiles) + + for _, file in ipairs(files) do + local name = file:sub(1, #file - 4) + local path = pathOrFiles.."."..name + + namespace.register(name, require(path)) + end + elseif (type(pathOrFiles == "table")) then + for _, path in ipairs(pathOrFiles) do + if (type(path) ~= "string") then + error("bad argument #2 to 'load' (string/table of strings expected, got table containing "..type(path)..")", 3) -- luacheck: ignore + end + + local name = path + + local dotIndex, slashIndex = path:match("^.*()%."), path:match("^.*()%/") + if (dotIndex or slashIndex) then + name = path:sub((dotIndex or slashIndex) + 1) + end + + namespace.register(name, require(path)) + end + end +end + +function Concord.loadComponents(pathOrFiles) + load(pathOrFiles, Concord.components) +end + +function Concord.loadSystems(pathOrFiles) + load(pathOrFiles, Concord.systems) +end + +function Concord.loadWorlds(pathOrFiles) + load(pathOrFiles, Concord.worlds) +end + +function Concord.loadAssemblages(pathOrFiles) + load(pathOrFiles, Concord.assemblages) +end + return Concord diff --git a/src/system.lua b/src/system.lua index f9b696c..871b9a5 100644 --- a/src/system.lua +++ b/src/system.lua @@ -2,7 +2,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Systems = require(PATH..".systems") local Pool = require(PATH..".pool") local System = {} @@ -36,20 +35,13 @@ System.mt = { --- Creates a new System prototype. -- @param ... Variable amounts of filters -- @return A new System prototype -function System.new(name, ...) - if (type(name) ~= "string") then - error("bad argument #1 to 'System.new' (string expected, got "..type(name)..")", 2) - end - +function System.new(...) local baseSystem = setmetatable({ - __name = name, __isBaseSystem = true, __filter = {...}, }, System.mt) baseSystem.__index = baseSystem - Systems.register(name, baseSystem) - return baseSystem end diff --git a/src/world.lua b/src/world.lua index 63ef1a9..2fe1ca8 100644 --- a/src/world.lua +++ b/src/world.lua @@ -2,7 +2,6 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Worlds = require(PATH..".worlds") local Type = require(PATH..".type") local List = require(PATH..".list") @@ -11,11 +10,7 @@ World.__index = World --- Creates a new World. -- @return The new World -function World.new(name) - if (type(name) ~= "string") then - error("bad argument #1 to 'World.new' (string expected, got "..type(name)..")", 2) - end - +function World.new() local world = setmetatable({ entities = List(), systems = List(), @@ -27,12 +22,9 @@ function World.new(name) __systemLookup = {}, - __name = name, __isWorld = true, }, World) - Worlds.register(name, world) - return world end diff --git a/test/components/test_comp_1.lua b/test/components/test_comp_1.lua new file mode 100644 index 0000000..ef9b98a --- /dev/null +++ b/test/components/test_comp_1.lua @@ -0,0 +1,6 @@ +local Component = require("src").component + +return Component(function(e, x, y) + e.x = x + e.y = y +end) \ No newline at end of file From a8bc92a9516567a063b731f51099e4315fe04fe8 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 22 Dec 2019 23:17:16 +0100 Subject: [PATCH 025/113] Allow for blank components --- main.lua | 6 ++---- src/component.lua | 7 +++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/main.lua b/main.lua index 1a5ed40..e7614b0 100644 --- a/main.lua +++ b/main.lua @@ -29,9 +29,7 @@ end) Components.register("test_comp_2", test_comp_2) -local test_comp_3 = Component(function(e, b) - e.b = b -end) +local test_comp_3 = Component() Components.register("test_comp_3", test_comp_3) @@ -72,7 +70,7 @@ entity :give(Components.test_comp_1, 100, 100) :remove(Components.test_comp_1) :give(Components.test_comp_1, 200, 100) - +:give(Components.test_comp_3, 200, 100) Worlds.testWorld:addEntity(entity) diff --git a/src/component.lua b/src/component.lua index 90dcdc6..a2beddb 100644 --- a/src/component.lua +++ b/src/component.lua @@ -7,8 +7,8 @@ Component.__index = Component -- @param populate A function that populates the Bag with values -- @return A Component object function Component.new(populate) - if not (type(populate) == "function") then - error("bad argument #1 to 'Component.new' (function expected, got "..type(populate)..")", 2) + if (type(populate) ~= "function" and type(populate) ~= "nil") then + error("bad argument #1 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) end local baseComponent = setmetatable({ @@ -22,6 +22,9 @@ function Component.new(populate) return baseComponent end +function Component:__populate() -- luacheck: ignore +end + --- Creates and initializes a new Component. -- @param ... The values passed to the populate function -- @return A new initialized Component From 38d461f8d6db60604e624a5c3cb8a72271bba73d Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 29 Dec 2019 15:05:19 +0100 Subject: [PATCH 026/113] Fixed entity lifetime problems --- main.lua | 95 ++++++++++++++++----------------- src/entity.lua | 56 +++++-------------- src/world.lua | 82 +++++++++++++++++----------- test/components/test_comp_1.lua | 6 --- 4 files changed, 110 insertions(+), 129 deletions(-) delete mode 100644 test/components/test_comp_1.lua diff --git a/main.lua b/main.lua index e7614b0..f8db366 100644 --- a/main.lua +++ b/main.lua @@ -20,77 +20,72 @@ local Entity = Concord.entity local World = Concord.world local Worlds = Concord.worlds -Concord.loadComponents("test/components") +local test_comp_1 = Component(function(e, a) + e.a = a +end) local test_comp_2 = Component(function(e, a) e.a = a end) -Components.register("test_comp_2", test_comp_2) - local test_comp_3 = Component() -Components.register("test_comp_3", test_comp_3) +local test_system_1 = System({test_comp_1}) -local test_system = System({Components.test_comp_1}) -Systems.register("test_system", test_system) +function test_system_1:init() + self.pool.onEntityAdded = function() + + -local function onEntityAdded(pool, e) -- luacheck: ignore - print("Added") -end - -local function onEntityRemoved(pool, e) -- luacheck: ignore - print("Removed") -end - -function test_system:init() - self.pool.onEntityAdded = onEntityAdded - self.pool.onEntityRemoved = onEntityRemoved -end - -function test_system:update(dt) -- luacheck: ignore - --[=[ - for _, v in ipairs(self.pool) do - print(v) + print("Added to test_system 1") end - ]=] + self.pool.onEntityRemoved = function() print("Removed from test_system 1") end end -function test_system:update2(dt) -- luacheck: ignore - --print(#self.pool) +function test_system_1:test() + print("Running test_system_1 with: " ..#self.pool) + + for _, e in ipairs(self.pool) do + local newE = Entity() + newE:give(test_comp_1) + self:getWorld():addEntity(newE) + + e:give(test_comp_2) + end end + +local test_system_2 = System({test_comp_2}) + +function test_system_2:init() + self.pool.onEntityAdded = function(pool, e) print("Added to test_system 2") e:remove(test_comp_1) end + self.pool.onEntityRemoved = function() print("Removed from test_system 2") end +end + +function test_system_2:test() + print("Running test_system_2 with: " ..#self.pool) + + for _, e in ipairs(self.pool) do + end +end + local world = World() -Worlds.register("testWorld", world) local entity = Entity() -entity -:give(Components.test_comp_1, 100, 100) -:remove(Components.test_comp_1) -:give(Components.test_comp_1, 200, 100) -:give(Components.test_comp_3, 200, 100) +entity:give(test_comp_1, 100, 100) -Worlds.testWorld:addEntity(entity) +world:addEntity(entity) -world:addSystem(Systems.test_system, "update") -world:addSystem(Systems.test_system, "update", "update2") +world:addSystem(test_system_1, "test") +world:addSystem(test_system_2, "test") -function love.update(dt) - world:flush() +print("Iteration: 1") +world:emit("test") - world:emit("update", dt) -end +print("Iteration: 2") +world:emit("test") -function love.keypressed(key) - if key == "q" then - entity:remove(Components.test_comp_1) - end - if key == "w" then - entity:give(Components.test_comp_1) - end - if key == "e" then - world:removeEntity(entity) - end -end \ No newline at end of file +print("Iteration: 3") +world:emit("test") \ No newline at end of file diff --git a/src/entity.lua b/src/entity.lua index 98d1c97..b1db60c 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -13,10 +13,6 @@ function Entity.new() local e = setmetatable({ __world = nil, - __addedComponents = {}, - __removedComponents = {}, - __operations = {}, - __components = {}, __isDirty = false, @@ -29,32 +25,22 @@ function Entity.new() return e end -local function giveOperation(e, component) - local baseComponent = component.__baseComponent - - e[baseComponent] = component - e.__components[baseComponent] = component -end - -local function removeOperation(e, baseComponent) - e[baseComponent] = nil - e.__components[baseComponent] = nil -end - local function give(e, baseComponent, ...) local component = baseComponent:__initialize(...) - e.__addedComponents[#e.__addedComponents + 1] = component - e.__operations[#e.__operations + 1] = giveOperation + e[baseComponent] = component + e.__components[baseComponent] = component e.__isDirty = true + e:__dirty() end local function remove(e, baseComponent) - e.__removedComponents[#e.__removedComponents + 1] = baseComponent - e.__operations[#e.__operations + 1] = removeOperation + e[baseComponent] = nil + e.__components[baseComponent] = nil e.__isDirty = true + e:__dirty() end --- Gives an Entity a component with values. @@ -108,36 +94,22 @@ function Entity:assemble(assemblage, ...) return self end -function Entity:__flush() - local addi, removei = 1, 1 - - for i = 1, #self.__operations do - local operation = self.__operations[i] - - if (operation == giveOperation) then - operation(self, self.__addedComponents[addi]) - self.__addedComponents[addi] = nil - addi = addi + 1 - elseif (operation == removeOperation) then - operation(self, self.__removedComponents[removei]) - self.__removedComponents[removei] = nil - removei = removei + 1 - end - - self.__operations[i] = nil - end -end - --- Destroys the Entity. -- @return self function Entity:destroy() - if self.world then - self.world:removeEntity(self) + if self.__world then + self.__world:removeEntity(self) end return self end +function Entity:__dirty() + if self.__world then + self.__world:__dirtyEntity(self) + end +end + --- Gets a Component from the Entity. -- @param component The Component to get -- @return The Bag from the Component diff --git a/src/world.lua b/src/world.lua index 2fe1ca8..bf0eaf1 100644 --- a/src/world.lua +++ b/src/world.lua @@ -17,8 +17,9 @@ function World.new() events = {}, - __added = {}, - __removed = {}, + __added = List(), __backAdded = List(), + __removed = List(), __backRemoved = List(), + __dirty = List(), __backDirty = List(), __systemLookup = {}, @@ -36,14 +37,16 @@ function World:addEntity(e) error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) end - if e.world then + if e.__world then error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2) end - e.world = self + e.__world = self e.__wasAdded = true - self.entities:add(e) + self.__added:add(e) + + --self.entities:add(e) return self end @@ -58,50 +61,65 @@ function World:removeEntity(e) e.__wasRemoved = true + self.__removed:add(e) + return self end +function World:__dirtyEntity(e) + if not self.__dirty:has(e) then + self.__dirty:add(e) + end +end + + -- @return self -function World:flush() +function World:__flush() + -- Switch buffers + self.__added, self.__backAdded = self.__backAdded, self.__added + self.__removed, self.__backRemoved = self.__backRemoved, self.__removed + self.__dirty, self.__backDirty = self.__backDirty, self.__dirty + local e - for i = 1, self.entities.size do - e = self.entities:get(i) - if e.__isDirty then - e:__flush() + -- Added + for i = 1, self.__backAdded.size do + e = self.__backAdded:get(i) - if (not e.__wasAdded) then -- The __wasAdded check below will handle this instead - for j = 1, self.systems.size do - self.systems:get(j):__evaluate(e) - end - end + self.entities:add(e) - e.__isDirty = false + for j = 1, self.systems.size do + self.systems:get(j):__evaluate(e) end - if e.__wasAdded then - e.__wasAdded = false + self:onEntityAdded(e) + end + self.__backAdded:clear() - for j = 1, self.systems.size do - self.systems:get(j):__evaluate(e) - end + -- Removed + for i = 1, self.__backRemoved.size do + e = self.__backRemoved:get(i) - self:onEntityAdded(e) + e.__world = nil + self.entities:remove(e) + + for j = 1, self.systems.size do + self.systems:get(j):__remove(e) end - if e.__wasRemoved then - e.world = nil - self.entities:remove(e) + self:onEntityRemoved(e) + end + self.__backRemoved:clear() - for j = 1, self.systems.size do - self.systems:get(j):__remove(e) - end + -- Dirty + for i = 1, self.__backDirty.size do + e = self.__backDirty:get(i) - e.__wasRemoved = false - - self:onEntityRemoved(e) + for j = 1, self.systems.size do + self.systems:get(j):__evaluate(e) end end + self.__backDirty:clear() return self end @@ -246,6 +264,8 @@ function World:emit(callbackName, ...) local listener = listeners[i] if listener.enabled then + self:__flush() + listener.system[listener.callback](listener.system, ...) end end diff --git a/test/components/test_comp_1.lua b/test/components/test_comp_1.lua deleted file mode 100644 index ef9b98a..0000000 --- a/test/components/test_comp_1.lua +++ /dev/null @@ -1,6 +0,0 @@ -local Component = require("src").component - -return Component(function(e, x, y) - e.x = x - e.y = y -end) \ No newline at end of file From 0183421c47784bb315fc90c6b60a1b047416fae3 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sun, 29 Dec 2019 15:56:20 +0100 Subject: [PATCH 027/113] Clean up entity and world. Remove list:get --- src/entity.lua | 9 +-------- src/list.lua | 7 ------- src/world.lua | 24 +++++++++--------------- test/components/test_comp_1.lua | 6 ++++++ tests/entityLifetime/init.lua | 0 tests/init.lua | 4 ++++ tests/requireModules/init.lua | 12 ++++++++++++ 7 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 test/components/test_comp_1.lua create mode 100644 tests/entityLifetime/init.lua create mode 100644 tests/init.lua create mode 100644 tests/requireModules/init.lua diff --git a/src/entity.lua b/src/entity.lua index b1db60c..755e75e 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -11,14 +11,9 @@ Entity.__index = Entity -- @return A new Entity function Entity.new() local e = setmetatable({ - __world = nil, - + __world = nil, __components = {}, - __isDirty = false, - __wasAdded = false, - __wasRemoved = false, - __isEntity = true, }, Entity) @@ -31,7 +26,6 @@ local function give(e, baseComponent, ...) e[baseComponent] = component e.__components[baseComponent] = component - e.__isDirty = true e:__dirty() end @@ -39,7 +33,6 @@ local function remove(e, baseComponent) e[baseComponent] = nil e.__components[baseComponent] = nil - e.__isDirty = true e:__dirty() end diff --git a/src/list.lua b/src/list.lua index b3e8fdf..89c4886 100644 --- a/src/list.lua +++ b/src/list.lua @@ -64,13 +64,6 @@ function List:remove(obj) return self end ---- Gets an object by numerical index. --- @param index The index to look at --- @return The object at the index -function List:get(index) - return self[index] -end - --- Gets if the List has the object. -- @param obj The object to search for -- true if the list has the object, false otherwise diff --git a/src/world.lua b/src/world.lua index bf0eaf1..ca97e1a 100644 --- a/src/world.lua +++ b/src/world.lua @@ -2,8 +2,8 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Type = require(PATH..".type") -local List = require(PATH..".list") +local Type = require(PATH..".type") +local List = require(PATH..".list") local World = {} World.__index = World @@ -42,12 +42,8 @@ function World:addEntity(e) end e.__world = self - e.__wasAdded = true - self.__added:add(e) - --self.entities:add(e) - return self end @@ -59,8 +55,6 @@ function World:removeEntity(e) error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) end - e.__wasRemoved = true - self.__removed:add(e) return self @@ -84,12 +78,12 @@ function World:__flush() -- Added for i = 1, self.__backAdded.size do - e = self.__backAdded:get(i) + e = self.__backAdded[i] self.entities:add(e) for j = 1, self.systems.size do - self.systems:get(j):__evaluate(e) + self.systems[j]:__evaluate(e) end self:onEntityAdded(e) @@ -98,13 +92,13 @@ function World:__flush() -- Removed for i = 1, self.__backRemoved.size do - e = self.__backRemoved:get(i) + e = self.__backRemoved[i] e.__world = nil self.entities:remove(e) for j = 1, self.systems.size do - self.systems:get(j):__remove(e) + self.systems[j]:__remove(e) end self:onEntityRemoved(e) @@ -113,10 +107,10 @@ function World:__flush() -- Dirty for i = 1, self.__backDirty.size do - e = self.__backDirty:get(i) + e = self.__backDirty[i] for j = 1, self.systems.size do - self.systems:get(j):__evaluate(e) + self.systems[j]:__evaluate(e) end end self.__backDirty:clear() @@ -163,7 +157,7 @@ function World:addSystem(baseSystem, callbackName, callback, enabled) -- Retroactively evaluate all entities for this system for i = 1, self.entities.size do - system:__evaluate(self.entities:get(i)) + system:__evaluate(self.entities[i]) end if callbackName then diff --git a/test/components/test_comp_1.lua b/test/components/test_comp_1.lua new file mode 100644 index 0000000..ef9b98a --- /dev/null +++ b/test/components/test_comp_1.lua @@ -0,0 +1,6 @@ +local Component = require("src").component + +return Component(function(e, x, y) + e.x = x + e.y = y +end) \ No newline at end of file diff --git a/tests/entityLifetime/init.lua b/tests/entityLifetime/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/tests/init.lua b/tests/init.lua new file mode 100644 index 0000000..5804170 --- /dev/null +++ b/tests/init.lua @@ -0,0 +1,4 @@ +local PATH = (...):gsub('%.init$', '') + +require(PATH..".requireModules") +require(PATH..".entityLifetime") \ No newline at end of file diff --git a/tests/requireModules/init.lua b/tests/requireModules/init.lua new file mode 100644 index 0000000..69efe13 --- /dev/null +++ b/tests/requireModules/init.lua @@ -0,0 +1,12 @@ +local Concord = require("src") + +assert(Concord.component +local Components = Concord.components + +local System = Concord.system +local Systems = Concord.systems + +local Entity = Concord.entity + +local World = Concord.world +local Worlds = Concord.worlds \ No newline at end of file From e5d6e1f4b970c5120fc7133a8f3996502ed88eb5 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 18:02:10 +0100 Subject: [PATCH 028/113] Remove temp tests --- tests/entityLifetime/init.lua | 0 tests/init.lua | 4 ---- tests/requireModules/init.lua | 12 ------------ 3 files changed, 16 deletions(-) delete mode 100644 tests/entityLifetime/init.lua delete mode 100644 tests/init.lua delete mode 100644 tests/requireModules/init.lua diff --git a/tests/entityLifetime/init.lua b/tests/entityLifetime/init.lua deleted file mode 100644 index e69de29..0000000 diff --git a/tests/init.lua b/tests/init.lua deleted file mode 100644 index 5804170..0000000 --- a/tests/init.lua +++ /dev/null @@ -1,4 +0,0 @@ -local PATH = (...):gsub('%.init$', '') - -require(PATH..".requireModules") -require(PATH..".entityLifetime") \ No newline at end of file diff --git a/tests/requireModules/init.lua b/tests/requireModules/init.lua deleted file mode 100644 index 69efe13..0000000 --- a/tests/requireModules/init.lua +++ /dev/null @@ -1,12 +0,0 @@ -local Concord = require("src") - -assert(Concord.component -local Components = Concord.components - -local System = Concord.system -local Systems = Concord.systems - -local Entity = Concord.entity - -local World = Concord.world -local Worlds = Concord.worlds \ No newline at end of file From fea5fc72230c613bb49710728702cdc269f4d34f Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 18:03:12 +0100 Subject: [PATCH 029/113] Fix World:clear --- main.lua | 3 --- src/world.lua | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/main.lua b/main.lua index f8db366..a671c69 100644 --- a/main.lua +++ b/main.lua @@ -35,9 +35,6 @@ local test_system_1 = System({test_comp_1}) function test_system_1:init() self.pool.onEntityAdded = function() - - - print("Added to test_system 1") end self.pool.onEntityRemoved = function() print("Removed from test_system 1") end diff --git a/src/world.lua b/src/world.lua index ca97e1a..59ab04d 100644 --- a/src/world.lua +++ b/src/world.lua @@ -272,7 +272,7 @@ end -- @return self function World:clear() for i = 1, self.entities.size do - self.removeEntity(self.entities[i]) + self:removeEntity(self.entities[i]) end return self From ce32d16b8d9637ac5cb5711d139297a02d968e3d Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 21:39:40 +0100 Subject: [PATCH 030/113] Allow Entity.new to take a world --- main.lua | 4 +--- src/entity.lua | 10 +++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/main.lua b/main.lua index a671c69..c48bd62 100644 --- a/main.lua +++ b/main.lua @@ -70,11 +70,9 @@ end local world = World() -local entity = Entity() +local entity = Entity(world) entity:give(test_comp_1, 100, 100) -world:addEntity(entity) - world:addSystem(test_system_1, "test") world:addSystem(test_system_2, "test") diff --git a/src/entity.lua b/src/entity.lua index 755e75e..ff60a66 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -9,7 +9,11 @@ Entity.__index = Entity --- Creates and initializes a new Entity. -- @return A new Entity -function Entity.new() +function Entity.new(world) + if (world ~= nil and not Type.isWorld(world)) then + error("bad argument #1 to 'Entity.new' (world/nil expected, got "..type(world)..")", 2) + end + local e = setmetatable({ __world = nil, __components = {}, @@ -17,6 +21,10 @@ function Entity.new() __isEntity = true, }, Entity) + if (world) then + world:addEntity(e) + end + return e end From f6468fbe6ba1d7ad8545f72bb637c24d03e63ae5 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 21:40:09 +0100 Subject: [PATCH 031/113] Add Entity:hasWorld --- src/entity.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/entity.lua b/src/entity.lua index ff60a66..86e42af 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -137,6 +137,10 @@ function Entity:getComponents() return self.__components end +function Entity:hasWorld() + return self.__world and true or false +end + function Entity:getWorld() return self.__world end From 6eaf66308d7e7a923fb38d9dae21c913042a231f Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 22:07:13 +0100 Subject: [PATCH 032/113] Revamp how systems are added to worlds --- main.lua | 3 +- src/world.lua | 176 +++++++++++++++++--------------------------------- 2 files changed, 61 insertions(+), 118 deletions(-) diff --git a/main.lua b/main.lua index c48bd62..1773142 100644 --- a/main.lua +++ b/main.lua @@ -73,8 +73,7 @@ local world = World() local entity = Entity(world) entity:give(test_comp_1, 100, 100) -world:addSystem(test_system_1, "test") -world:addSystem(test_system_2, "test") +world:addSystems(test_system_1, test_system_2) print("Iteration: 1") world:emit("test") diff --git a/src/world.lua b/src/world.lua index 59ab04d..c9bba3c 100644 --- a/src/world.lua +++ b/src/world.lua @@ -15,7 +15,7 @@ function World.new() entities = List(), systems = List(), - events = {}, + events = {}, __added = List(), __backAdded = List(), __removed = List(), __backRemoved = List(), @@ -69,6 +69,11 @@ end -- @return self function World:__flush() + -- Early out + if (self.__added.size == 0 and self.__removed.size == 0 and self.__dirty.size == 0) then + return self + end + -- Switch buffers self.__added, self.__backAdded = self.__backAdded, self.__added self.__removed, self.__backRemoved = self.__backRemoved, self.__removed @@ -76,7 +81,7 @@ function World:__flush() local e - -- Added + -- Process added entities for i = 1, self.__backAdded.size do e = self.__backAdded[i] @@ -90,7 +95,7 @@ function World:__flush() end self.__backAdded:clear() - -- Removed + -- Process removed entities for i = 1, self.__backRemoved.size do e = self.__backRemoved[i] @@ -105,7 +110,7 @@ function World:__flush() end self.__backRemoved:clear() - -- Dirty + -- Process dirty entities for i = 1, self.__backDirty.size do e = self.__backDirty[i] @@ -118,6 +123,55 @@ function World:__flush() return self end +local blacklistedSystemMethods = { + "init", +} + +function World:addSystem(baseSystem) + if (not Type.isBaseSystem(baseSystem)) then + error("bad argument #"..i.." to 'World:addSystems' (baseSystem expected, got "..type(baseSystem)..")", 2) + end + + -- TODO: Check if baseSystem was already added + + -- Create instance of system + local system = baseSystem(self) + + self.__systemLookup[baseSystem] = system + self.systems:add(system) + + for callbackName, callback in pairs(baseSystem) do + -- Skip callback if its blacklisted + if (not blacklistedSystemMethods[callbackName]) then + -- Make container for all listeners of the callback if it does not exist yet + if (not self.events[callbackName]) then + self.events[callbackName] = {} + end + + -- Add callback to listeners + local listeners = self.events[callbackName] + listeners[#listeners + 1] = { + system = system, + callback = callback, + } + end + end + + -- Evaluate all existing entities + for j = 1, self.entities.size do + system:__evaluate(self.entities[j]) + end +end + +function World:addSystems(...) + for i = 1, select("#", ...) do + local baseSystem = select(i, ...) + + self:addSystem(baseSystem) + end +end + + function World:hasSystem(baseSystem) if not Type.isBaseSystem(baseSystem) then error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) @@ -134,114 +188,6 @@ function World:getSystem(baseSystem) return self.__systemLookup[baseSystem] end ---- Adds a System to the World. --- @param baseSystem The BaseSystem of the system to add --- @param callbackName The callbackName to register to --- @param callback The function name to call. Defaults to callbackName --- @param enabled If the system is enabled. Defaults to true --- @return self -function World:addSystem(baseSystem, callbackName, callback, enabled) - if not Type.isBaseSystem(baseSystem) then - error("bad argument #1 to 'World:addSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) - end - - local system = self.__systemLookup[baseSystem] - if (not system) then - -- System was not created for this world yet, so we create it ourselves - system = baseSystem(self) - - self.__systemLookup[baseSystem] = system - - self.systems:add(system) - end - - -- Retroactively evaluate all entities for this system - for i = 1, self.entities.size do - system:__evaluate(self.entities[i]) - end - - if callbackName then - self.events[callbackName] = self.events[callbackName] or {} - - local i = #self.events[callbackName] + 1 - self.events[callbackName][i] = { - system = system, - callback = callback or callbackName, - enabled = enabled == nil or enabled, - } - - if enabled == nil or enabled then - system:enabledCallback(callback or callbackName) - end - end - - return self -end - ---- Enables a System in the World. --- @param system The System to enable --- @param callbackName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @return self -function World:enableSystem(system, callbackName, callback) - if not Type.isSystem(system) then - error("bad argument #1 to 'World:enableSystem' (System expected, got "..type(system)..")", 2) - end - - return self:setSystem(system, callbackName, callback, true) -end - ---- Disables a System in the World. --- @param system The System to disable --- @param eventName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @return self -function World:disableSystem(system, callbackName, callback) - if not Type.isSystem(system) then - error("bad argument #1 to 'World:disableSystem' (System expected, got "..type(system)..")", 2) - end - - return self:setSystem(system, callbackName, callback, false) -end - ---- Sets a System 'enable' in the World. --- @param system The System to set --- @param eventName The Event it was registered to --- @param callback The callback it was registered with. Defaults to eventName --- @param enable The state to set it to --- @return self -function World:setSystem(system, callbackName, callback, enable) - if not Type.isSystem(system) then - error("bad argument #1 to 'World:setSystem' (System expected, got "..type(system)..")", 2) - end - - callback = callback or callbackName - - if callback then - local listeners = self.events[callbackName] - - if listeners then - for i = 1, #listeners do - local listener = listeners[i] - - if listener.system == system and listener.callback == callback then - if enable and not listener.enabled then - system:enabledCallback(callback) - elseif not enable and listener.enabled then - system:disabledCallback(callback) - end - - listener.enabled = enable - - break - end - end - end - end - - return self -end - --- Emits an Event in the World. -- @param eventName The Event that should be emitted -- @param ... Parameters passed to listeners @@ -257,11 +203,9 @@ function World:emit(callbackName, ...) for i = 1, #listeners do local listener = listeners[i] - if listener.enabled then - self:__flush() + self:__flush() - listener.system[listener.callback](listener.system, ...) - end + listener.callback(listener.system, ...) end end From 04a35a117eafba6589001189e2f3b0f0fba7240d Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 22:19:01 +0100 Subject: [PATCH 033/113] Allow systems to be disabled --- src/system.lua | 34 +++++++++++++++++++++++++++++++--- src/world.lua | 8 +++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/system.lua b/src/system.lua index 871b9a5..e77934e 100644 --- a/src/system.lua +++ b/src/system.lua @@ -9,6 +9,8 @@ System.mt = { __index = System, __call = function(baseSystem, world) local system = setmetatable({ + __enabled = true, + __pools = {}, __world = world, @@ -94,6 +96,32 @@ function System:clear() end end +function System:enable() + self:setEnabled(true) +end + +function System:disable() + self:setEnabled(false) +end + +function System:toggleEnable() + self:setEnabled(not self.__enabled) +end + +function System:setEnabled(enable) + if (not self.__enabled and enable) then + self.__enabled = true + self:onEnabledCallback() + elseif (self.__enabled and not enable) then + self.__enabled = false + self:onDisabledCallback() + end +end + +function System:isEnabled() + return self.__enabled +end + --- Returns the World the System is in. -- @return The world the system is in function System:getWorld() @@ -107,16 +135,16 @@ end -- Default callback for when a System's callback is enabled. -- @param callbackName The name of the callback that was enabled -function System:enabledCallback(callbackName) -- luacheck: ignore +function System:onEnabledCallback(callbackName) -- luacheck: ignore end -- Default callback for when a System's callback is disabled. -- @param callbackName The name of the callback that was disabled -function System:disabledCallback(callbackName) -- luacheck: ignore +function System:onDisabledCallback(callbackName) -- luacheck: ignore end return setmetatable(System, { __call = function(_, ...) return System.new(...) end, -}) +}) \ No newline at end of file diff --git a/src/world.lua b/src/world.lua index c9bba3c..9ce0215 100644 --- a/src/world.lua +++ b/src/world.lua @@ -129,7 +129,7 @@ local blacklistedSystemMethods = { function World:addSystem(baseSystem) if (not Type.isBaseSystem(baseSystem)) then - error("bad argument #"..i.." to 'World:addSystems' (baseSystem expected, got "..type(baseSystem)..")", 2) + error("bad argument #1 to 'World:addSystems' (baseSystem expected, got "..type(baseSystem)..")", 2) end -- TODO: Check if baseSystem was already added @@ -203,9 +203,11 @@ function World:emit(callbackName, ...) for i = 1, #listeners do local listener = listeners[i] - self:__flush() + if (listener.system.__enabled) then + self:__flush() - listener.callback(listener.system, ...) + listener.callback(listener.system, ...) + end end end From 56b52445418fbb518f79e6335d7a394cdf2ae8b1 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 22:20:31 +0100 Subject: [PATCH 034/113] Let System functions return self --- src/system.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/system.lua b/src/system.lua index e77934e..28208d2 100644 --- a/src/system.lua +++ b/src/system.lua @@ -78,6 +78,8 @@ function System:__evaluate(e) pool:remove(e) end end + + return self end --- Remove an Entity from the System. @@ -88,24 +90,34 @@ function System:__remove(e) pool:remove(e) end end + + return self end function System:clear() for i = 1, #self.__pools do self.__pools[i]:clear() end + + return self end function System:enable() self:setEnabled(true) + + return self end function System:disable() self:setEnabled(false) + + return self end function System:toggleEnable() self:setEnabled(not self.__enabled) + + return self end function System:setEnabled(enable) @@ -116,6 +128,8 @@ function System:setEnabled(enable) self.__enabled = false self:onDisabledCallback() end + + return self end function System:isEnabled() From 6aeb91d9840c151ccf31f8629ea7c93bdbb4d42e Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 23:01:04 +0100 Subject: [PATCH 035/113] Add optional optimization for worlds and systems --- main.lua | 75 ++++++++++++++++---------------------------------- src/system.lua | 15 ++++++++-- src/utils.lua | 9 ++++++ src/world.lua | 16 +++++++++-- 4 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 src/utils.lua diff --git a/main.lua b/main.lua index 1773142..ff2ec47 100644 --- a/main.lua +++ b/main.lua @@ -9,77 +9,48 @@ require(file) local Concord = require("src") -local Component = Concord.component -local Components = Concord.components - -local System = Concord.system -local Systems = Concord.systems - -local Entity = Concord.entity - -local World = Concord.world -local Worlds = Concord.worlds - +local Component = Concord.component +local System = Concord.system +local Entity = Concord.entity +local World = Concord.world local test_comp_1 = Component(function(e, a) e.a = a end) -local test_comp_2 = Component(function(e, a) - e.a = a -end) - -local test_comp_3 = Component() - local test_system_1 = System({test_comp_1}) - -function test_system_1:init() - self.pool.onEntityAdded = function() - print("Added to test_system 1") - end - self.pool.onEntityRemoved = function() print("Removed from test_system 1") end -end +local test_system_2 = System({test_comp_1}) +local test_system_3 = System({test_comp_1}) function test_system_1:test() - print("Running test_system_1 with: " ..#self.pool) - - for _, e in ipairs(self.pool) do - local newE = Entity() - newE:give(test_comp_1) - self:getWorld():addEntity(newE) - - e:give(test_comp_2) + for _, _ in ipairs(self.pool) do end end - - -local test_system_2 = System({test_comp_2}) - -function test_system_2:init() - self.pool.onEntityAdded = function(pool, e) print("Added to test_system 2") e:remove(test_comp_1) end - self.pool.onEntityRemoved = function() print("Removed from test_system 2") end -end - function test_system_2:test() - print("Running test_system_2 with: " ..#self.pool) + for _, _ in ipairs(self.pool) do + end +end - for _, e in ipairs(self.pool) do +function test_system_3:test() + for _, _ in ipairs(self.pool) do end end local world = World() -local entity = Entity(world) -entity:give(test_comp_1, 100, 100) +world:addSystems(test_system_1, test_system_2, test_system_3) -world:addSystems(test_system_1, test_system_2) +for _ = 1, 100 do + local entity = Entity(world) + entity:give(test_comp_1, 100, 100) +end -print("Iteration: 1") -world:emit("test") -print("Iteration: 2") -world:emit("test") +local start = love.timer.getTime() +for _ = 1, 1000000 do + world:emit("test") +end +local stop = love.timer.getTime() -print("Iteration: 3") -world:emit("test") \ No newline at end of file +print("Time taken: " .. stop - start) \ No newline at end of file diff --git a/src/system.lua b/src/system.lua index 28208d2..4839f70 100644 --- a/src/system.lua +++ b/src/system.lua @@ -2,9 +2,13 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Pool = require(PATH..".pool") +local Pool = require(PATH..".pool") +local Utils = require(PATH..".utils") + +local System = { + ENABLE_OPTIMIZATION = true, +} -local System = {} System.mt = { __index = System, __call = function(baseSystem, world) @@ -18,6 +22,13 @@ System.mt = { __isBaseSystem = false, -- Overwrite value from baseSystem }, baseSystem) + -- Optimization: We deep copy the World class into our instance of a world. + -- 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(System, system) + end + for _, filter in pairs(baseSystem.__filter) do local pool = system:__buildPool(filter) if not system[pool.name] then diff --git a/src/utils.lua b/src/utils.lua new file mode 100644 index 0000000..43adf6c --- /dev/null +++ b/src/utils.lua @@ -0,0 +1,9 @@ +local Utils = {} + +function Utils.shallowCopy(orig, target) + for key, value in pairs(orig) do + target[key] = value + end +end + +return Utils \ No newline at end of file diff --git a/src/world.lua b/src/world.lua index 9ce0215..bd79ebd 100644 --- a/src/world.lua +++ b/src/world.lua @@ -2,10 +2,13 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Type = require(PATH..".type") -local List = require(PATH..".list") +local Type = require(PATH..".type") +local List = require(PATH..".list") +local Utils = require(PATH..".utils") -local World = {} +local World = { + ENABLE_OPTIMIZATION = true, +} World.__index = World --- Creates a new World. @@ -26,6 +29,13 @@ function World.new() __isWorld = true, }, World) + -- Optimization: We deep copy the World class into our instance of a world. + -- 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 (World.ENABLE_OPTIMIZATION) then + Utils.shallowCopy(World, world) + end + return world end From f6669b2e6385c33c165625bc0f4b41fdc8b699b4 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Fri, 3 Jan 2020 23:12:56 +0100 Subject: [PATCH 036/113] Improve internal style --- src/assemblage.lua | 6 ++++-- src/component.lua | 10 ++++++--- src/entity.lua | 6 ++++-- src/list.lua | 52 +++++++++++++++++++++++++++++----------------- src/pool.lua | 30 +++++++++++++++++--------- src/system.lua | 16 +++++++------- src/world.lua | 24 +++++++++++---------- 7 files changed, 89 insertions(+), 55 deletions(-) diff --git a/src/assemblage.lua b/src/assemblage.lua index 79b3cbb..241525d 100644 --- a/src/assemblage.lua +++ b/src/assemblage.lua @@ -1,14 +1,16 @@ --- Assemblage local Assemblage = {} -Assemblage.__index = Assemblage +Assemblage.__mt = { + __index = Assemblage, +} function Assemblage.new(assemble) local assemblage = setmetatable({ __assemble = assemble, __isAssemblage = true, - }, Assemblage) + }, Assemblage.__mt) return assemblage end diff --git a/src/component.lua b/src/component.lua index a2beddb..d288815 100644 --- a/src/component.lua +++ b/src/component.lua @@ -1,7 +1,9 @@ --- Component local Component = {} -Component.__index = Component +Component.__mt = { + __index = Component, +} --- Creates a new Component. -- @param populate A function that populates the Bag with values @@ -15,9 +17,11 @@ function Component.new(populate) __populate = populate, __isBaseComponent = true, - }, Component) + }, Component.__mt) - baseComponent.__mt = {__index = baseComponent} + baseComponent.__mt = { + __index = baseComponent + } return baseComponent end diff --git a/src/entity.lua b/src/entity.lua index 86e42af..38cc60d 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -5,7 +5,9 @@ local PATH = (...):gsub('%.[^%.]+$', '') local Type = require(PATH..".type") local Entity = {} -Entity.__index = Entity +Entity.__mt = { + __index = Entity, +} --- Creates and initializes a new Entity. -- @return A new Entity @@ -19,7 +21,7 @@ function Entity.new(world) __components = {}, __isEntity = true, - }, Entity) + }, Entity.__mt) if (world) then world:addEntity(e) diff --git a/src/list.lua b/src/list.lua index 89c4886..94fee52 100644 --- a/src/list.lua +++ b/src/list.lua @@ -1,35 +1,22 @@ --- List local List = {} -local mt = {__index = List} +List.__mt = { + __index = List +} --- Creates a new List. -- @return A new list function List.new() return setmetatable({ size = 0, - }, mt) -end - ---- Clears the List completely. --- @return self -function List:clear() - for i = 1, self.size do - local o = self[i] - - self[o] = nil - self[i] = nil - end - - self.size = 0 - - return self + }, List.__mt) end --- Adds an object to the List. -- @param obj The object to add -- @return self -function List:add(obj) -- obj can not be a number and also not the string "size" +function List:__add(obj) -- obj can not be a number and also not the string "size" local size = self.size + 1 self[size] = obj @@ -42,7 +29,7 @@ end --- Removes an object from the List. -- @param obj The object to remove -- @return self -function List:remove(obj) +function List:__remove(obj) local index = self[obj] if not index then return end local size = self.size @@ -64,6 +51,21 @@ function List:remove(obj) return self end +--- Clears the List completely. +-- @return self +function List:__clear() + for i = 1, self.size do + local o = self[i] + + self[o] = nil + self[i] = nil + end + + self.size = 0 + + return self +end + --- Gets if the List has the object. -- @param obj The object to search for -- true if the list has the object, false otherwise @@ -71,6 +73,18 @@ function List:has(obj) return self[obj] and true or false end +function List:get(i) + return self[i] +end + +function List:indexOf(obj) + if (not self[obj]) then + error("bad argument #1 to 'List:indexOf' (Object was not in List)", 2) + end + + return self[obj] +end + return setmetatable(List, { __call = function() return List.new() diff --git a/src/pool.lua b/src/pool.lua index 92373e2..1b7be39 100644 --- a/src/pool.lua +++ b/src/pool.lua @@ -5,17 +5,19 @@ local PATH = (...):gsub('%.[^%.]+$', '') local List = require(PATH..".list") local Pool = {} -Pool.__index = Pool +Pool.__mt = { + __index = Pool, +} --- Creates a new Pool -- @param name Identifier for the Pool. -- @param filter Table containing the required Components -- @return The new Pool function Pool.new(name, filter) - local pool = setmetatable(List(), Pool) + local pool = setmetatable(List(), Pool.__mt) - pool.name = name - pool.filter = filter + pool.__name = name + pool.__filter = filter pool.__isPool = true @@ -25,8 +27,8 @@ end --- Checks if an Entity is eligible for the Pool. -- @param e The Entity to check -- @return True if the entity is eligible, false otherwise -function Pool:eligible(e) - for _, component in ipairs(self.filter) do +function Pool:__eligible(e) + for _, component in ipairs(self.__filter) do if not e[component] then return false end @@ -35,16 +37,24 @@ function Pool:eligible(e) return true end -function Pool:add(e) - List.add(self, e) +function Pool:__add(e) + List.__add(self, e) self:onEntityAdded(e) end -function Pool:remove(e) - List.remove(self, e) +function Pool:__remove(e) + List.__remove(self, e) self:onEntityRemoved(e) end +function Pool:getName() + return self.__name +end + +function Pool:getFilter() + return self.__filter +end + function Pool:onEntityAdded(e) -- luacheck: ignore end diff --git a/src/system.lua b/src/system.lua index 4839f70..4e0b778 100644 --- a/src/system.lua +++ b/src/system.lua @@ -9,7 +9,7 @@ local System = { ENABLE_OPTIMIZATION = true, } -System.mt = { +System.mt = { __index = System, __call = function(baseSystem, world) local system = setmetatable({ @@ -31,8 +31,8 @@ System.mt = { for _, filter in pairs(baseSystem.__filter) do local pool = system:__buildPool(filter) - if not system[pool.name] then - system[pool.name] = pool + 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.") @@ -81,12 +81,12 @@ end function System:__evaluate(e) for _, pool in ipairs(self.__pools) do local has = pool:has(e) - local eligible = pool:eligible(e) + local eligible = pool:__eligible(e) if not has and eligible then - pool:add(e) + pool:__add(e) elseif has and not eligible then - pool:remove(e) + pool:__remove(e) end end @@ -98,7 +98,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 @@ -107,7 +107,7 @@ end function System:clear() for i = 1, #self.__pools do - self.__pools[i]:clear() + self.__pools[i]:__clear() end return self diff --git a/src/world.lua b/src/world.lua index bd79ebd..f15a14d 100644 --- a/src/world.lua +++ b/src/world.lua @@ -9,7 +9,9 @@ local Utils = require(PATH..".utils") local World = { ENABLE_OPTIMIZATION = true, } -World.__index = World +World.__mt = { + __index = World, +} --- Creates a new World. -- @return The new World @@ -27,7 +29,7 @@ function World.new() __systemLookup = {}, __isWorld = true, - }, World) + }, World.__mt) -- Optimization: We deep copy the World class into our instance of a world. -- This grants slightly faster access times at the cost of memory. @@ -52,7 +54,7 @@ function World:addEntity(e) end e.__world = self - self.__added:add(e) + self.__added:__add(e) return self end @@ -65,14 +67,14 @@ 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 function World:__dirtyEntity(e) if not self.__dirty:has(e) then - self.__dirty:add(e) + self.__dirty:__add(e) end end @@ -95,7 +97,7 @@ function World:__flush() for i = 1, self.__backAdded.size do e = self.__backAdded[i] - self.entities:add(e) + self.entities:__add(e) for j = 1, self.systems.size do self.systems[j]:__evaluate(e) @@ -103,14 +105,14 @@ function World:__flush() 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) + self.entities:__remove(e) for j = 1, self.systems.size do self.systems[j]:__remove(e) @@ -118,7 +120,7 @@ function World:__flush() self:onEntityRemoved(e) end - self.__backRemoved:clear() + self.__backRemoved:__clear() -- Process dirty entities for i = 1, self.__backDirty.size do @@ -128,7 +130,7 @@ function World:__flush() self.systems[j]:__evaluate(e) end end - self.__backDirty:clear() + self.__backDirty:__clear() return self end @@ -148,7 +150,7 @@ function World:addSystem(baseSystem) local system = baseSystem(self) self.__systemLookup[baseSystem] = system - self.systems:add(system) + self.systems:__add(system) for callbackName, callback in pairs(baseSystem) do -- Skip callback if its blacklisted From 69a9e83759175247c8f8b6dca0c5ef05f8bc8ea6 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 01:04:18 +0100 Subject: [PATCH 037/113] Document full project. Lots of small fixes --- src/assemblage.lua | 10 ++++ src/assemblages.lua | 10 ++-- src/component.lua | 29 ++++++----- src/components.lua | 16 +++--- src/entity.lua | 117 +++++++++++++++++++++++++++----------------- src/init.lua | 20 +++++++- src/list.lua | 23 ++++++--- src/pool.lua | 27 ++++++++-- src/system.lua | 76 ++++++++++++++++------------ src/systems.lua | 14 ++++-- src/type.lua | 32 ++++++++++-- src/utils.lua | 6 +++ src/world.lua | 112 ++++++++++++++++++++++++++++-------------- src/worlds.lua | 6 ++- 14 files changed, 342 insertions(+), 156 deletions(-) diff --git a/src/assemblage.lua b/src/assemblage.lua index 241525d..c2303f2 100644 --- a/src/assemblage.lua +++ b/src/assemblage.lua @@ -1,10 +1,15 @@ --- Assemblage +-- An Assemblage is a function that 'makes' an entity something. +-- It does this by :give'ing or :ensure'ing Components, or by :assemble'ing the Entity. local Assemblage = {} Assemblage.__mt = { __index = Assemblage, } +--- Creates a new Assemblage. +-- @param assemble Function that assembles an Entity +-- @return A new Assemblage function Assemblage.new(assemble) local assemblage = setmetatable({ __assemble = assemble, @@ -15,6 +20,11 @@ function Assemblage.new(assemble) return assemblage end +--- Assembles an Entity. +-- @see Entity:assemble +-- @param e Entity to assemble +-- @param ... Varargs to pass to the assemble function +-- @ return self function Assemblage:assemble(e, ...) self.__assemble(e, ...) diff --git a/src/assemblages.lua b/src/assemblages.lua index f86f629..f34e3b9 100644 --- a/src/assemblages.lua +++ b/src/assemblages.lua @@ -1,4 +1,5 @@ --- Assemblages +--- Assemblages +-- Container for registered Assemblages local PATH = (...):gsub('%.[^%.]+$', '') @@ -6,17 +7,20 @@ local Type = require(PATH..".type") local Assemblages = {} +--- Registers an Assemblage. +-- @param name Name to register under +-- @param 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(world)..")", 3) + 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.."' is already registerd)", 3) + error("bad argument #2 to 'Assemblages.register' (Assemblage with name '"..name.."' was already registerd)", 3) end Assemblages[name] = assemblage diff --git a/src/component.lua b/src/component.lua index d288815..bf862a0 100644 --- a/src/component.lua +++ b/src/component.lua @@ -1,43 +1,46 @@ --- Component +-- A Component is a pure data container. +-- A Component is contained by a single entity. local Component = {} Component.__mt = { __index = Component, } ---- Creates a new Component. --- @param populate A function that populates the Bag with values --- @return A Component object +--- Creates a new ComponentClass. +-- @param populate Function that populates a Component with values +-- @return A new ComponentClass function Component.new(populate) if (type(populate) ~= "function" and type(populate) ~= "nil") then error("bad argument #1 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) end - local baseComponent = setmetatable({ + local componentClass = setmetatable({ __populate = populate, - __isBaseComponent = true, + __isComponentClass = true, }, Component.__mt) - baseComponent.__mt = { - __index = baseComponent + componentClass.__mt = { + __index = componentClass } - return baseComponent + return componentClass end +--- Internal: Populates a Component with values function Component:__populate() -- luacheck: ignore end ---- Creates and initializes a new Component. --- @param ... The values passed to the populate function --- @return A new initialized Component +--- Internal: Creates and populates a new Component. +-- @param ... Varargs passed to the populate function +-- @return A new populated Component function Component:__initialize(...) local component = setmetatable({ - __baseComponent = self, + __componentClass = self, __isComponent = true, - __isBaseComponent = false, + __isComponentClass = false, }, self) self.__populate(component, ...) diff --git a/src/components.lua b/src/components.lua index 2249774..76780de 100644 --- a/src/components.lua +++ b/src/components.lua @@ -1,4 +1,5 @@ -- Components +-- Container for registered ComponentClasss local PATH = (...):gsub('%.[^%.]+$', '') @@ -6,24 +7,27 @@ local Type = require(PATH..".type") local Components = {} -function Components.register(name, baseComponent) +--- Registers a ComponentClass. +-- @param name Name to register under +-- @param 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) end - if (not Type.isBaseComponent(baseComponent)) then - error("bad argument #2 to 'Components.register' (BaseComponent expected, got "..type(baseComponent)..")", 3) + if (not Type.isComponentClass(componentClass)) then + error("bad argument #2 to 'Components.register' (ComponentClass expected, got "..type(componentClass)..")", 3) end if (rawget(Components, name)) then - error("bad argument #2 to 'Components.register' (BaseComponent with name '"..name.."' is already registerd)", 3) + error("bad argument #2 to 'Components.register' (ComponentClass with name '"..name.."' was already registerd)", 3) end - Components[name] = baseComponent + Components[name] = componentClass end return setmetatable(Components, { __index = function(_, name) - error("Attempt to index BaseComponent '"..tostring(name).."' that does not exist / was not registered", 2) + error("Attempt to index ComponentClass '"..tostring(name).."' that does not exist / was not registered", 2) end }) \ No newline at end of file diff --git a/src/entity.lua b/src/entity.lua index 38cc60d..c084039 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -1,4 +1,7 @@ --- Entity +-- Entities are the concrete objects that exist in your project. +-- An Entity have Components and are processed by Systems. +-- An Entity is contained by a maximum of 1 World. local PATH = (...):gsub('%.[^%.]+$', '') @@ -9,7 +12,8 @@ Entity.__mt = { __index = Entity, } ---- Creates and initializes a new Entity. +--- Creates a new Entity. Optionally adds it to a World. +-- @param world Optional World to add the entity to -- @return A new Entity function Entity.new(world) if (world ~= nil and not Type.isWorld(world)) then @@ -30,63 +34,73 @@ function Entity.new(world) return e end -local function give(e, baseComponent, ...) - local component = baseComponent:__initialize(...) +local function give(e, componentClass, ...) + local component = componentClass:__initialize(...) - e[baseComponent] = component - e.__components[baseComponent] = component + e[componentClass] = component + e.__components[componentClass] = component e:__dirty() end -local function remove(e, baseComponent) - e[baseComponent] = nil - e.__components[baseComponent] = nil +local function remove(e, componentClass) + e[componentClass] = nil + e.__components[componentClass] = nil e:__dirty() end ---- Gives an Entity a component with values. --- @param component The Component to add --- @param ... The values passed to the Component +--- Gives an Entity a Component. +-- If the Component already exists, it's overridden by this new Component +-- @param componentClass ComponentClass to add an instance of +-- @param ... varargs passed to the Component's populate function -- @return self -function Entity:give(baseComponent, ...) - if not Type.isBaseComponent(baseComponent) then - error("bad argument #1 to 'Entity:give' (BaseComponent expected, got "..type(baseComponent)..")", 2) +function Entity:give(componentClass, ...) + if not Type.isComponentClass(componentClass) then + error("bad argument #1 to 'Entity:give' (ComponentClass expected, got "..type(componentClass)..")", 2) end - give(self, baseComponent, ...) + give(self, componentClass, ...) return self end -function Entity:ensure(baseComponent, ...) - if not Type.isBaseComponent(baseComponent) then - error("bad argument #1 to 'Entity:ensure' (BaseComponent expected, got "..type(baseComponent)..")", 2) +--- Ensures an Entity to have a Component. +-- If the Component already exists, no action is taken +-- @param componentClass ComponentClass to add an instance of +-- @param ... varargs passed to the Component's populate function +-- @return self +function Entity:ensure(componentClass, ...) + if not Type.isComponentClass(componentClass) then + error("bad argument #1 to 'Entity:ensure' (ComponentClass expected, got "..type(componentClass)..")", 2) end - if self[baseComponent] then + if self[componentClass] then return self end - give(self, baseComponent, ...) + give(self, componentClass, ...) return self end ---- Removes a component from an Entity. --- @param component The Component to remove +--- Removes a Component from an Entity. +-- @param componentClass ComponentClass of the Component to remove -- @return self -function Entity:remove(baseComponent) - if not Type.isBaseComponent(baseComponent) then - error("bad argument #1 to 'Entity:remove' (BaseComponent expected, got "..type(baseComponent)..")") +function Entity:remove(componentClass) + if not Type.isComponentClass(componentClass) then + error("bad argument #1 to 'Entity:remove' (ComponentClass expected, got "..type(componentClass)..")") end - remove(self, baseComponent) + remove(self, componentClass) return self end +--- Assembles an Entity. +-- @see Assemblage:assemble +-- @param assemblage Assemblage to assemble with +-- @param ... Varargs to pass to the Assemblage's assemble function. function Entity:assemble(assemblage, ...) if not Type.isAssemblage(assemblage) then error("bad argument #1 to 'Entity:assemble' (Assemblage expected, got "..type(assemblage)..")") @@ -98,6 +112,7 @@ function Entity:assemble(assemblage, ...) end --- Destroys the Entity. +-- Removes the Entity from it's World if it's in one. -- @return self function Entity:destroy() if self.__world then @@ -107,42 +122,54 @@ function Entity:destroy() return self end +--- Internal: Tells the World it's in that this Entity is dirty. +-- @return self function Entity:__dirty() if self.__world then self.__world:__dirtyEntity(self) end + + return self +end + +--- Returns true if the Entity has a Component. +-- @param componentClass ComponentClass of the Component to check +-- @return True if the Entity has the Component, false otherwise +function Entity:has(componentClass) + if not Type.isComponentClass(componentClass) then + error("bad argument #1 to 'Entity:has' (ComponentClass expected, got "..type(componentClass)..")") + end + + return self[componentClass] ~= nil end --- Gets a Component from the Entity. --- @param component The Component to get --- @return The Bag from the Component -function Entity:get(baseComponent) - if not Type.isBaseComponent(baseComponent) then - error("bad argument #1 to 'Entity:get' (BaseComponent expected, got "..type(baseComponent)..")") +-- @param componentClass ComponentClass of the Component to get +-- @return The Component +function Entity:get(componentClass) + if not Type.isComponentClass(componentClass) then + error("bad argument #1 to 'Entity:get' (ComponentClass expected, got "..type(componentClass)..")") end - return self[baseComponent] -end - ---- Returns true if the Entity has the Component. --- @param component The Component to check against --- @return True if the entity has the Bag. False otherwise -function Entity:has(baseComponent) - if not Type.isBaseComponent(baseComponent) then - error("bad argument #1 to 'Entity:has' (BaseComponent expected, got "..type(baseComponent)..")") - end - - return self[baseComponent] ~= nil + return self[componentClass] end +--- Returns a table of all Components the Entity has. +-- Warning: Do not modify this table. +-- Use Entity:give/ensure/remove instead +-- @return Table of all Components the Entity has function Entity:getComponents() return self.__components end -function Entity:hasWorld() +--- Returns true if the Entity is in a World. +-- @return True if the Entity is in a World, false otherwise +function Entity:inWorld() return self.__world and true or false end +--- Returns the World the Entity is in. +-- @return The World the Entity is in. function Entity:getWorld() return self.__world end @@ -151,4 +178,4 @@ return setmetatable(Entity, { __call = function(_, ...) return Entity.new(...) end, -}) +}) \ No newline at end of file diff --git a/src/init.lua b/src/init.lua index 66916db..b373989 100644 --- a/src/init.lua +++ b/src/init.lua @@ -47,13 +47,13 @@ Concord.assemblages = require(PATH..".assemblages") local function load(pathOrFiles, namespace) if (type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table") then - error("bad argument #1 to 'load' (string/table of strings expected, got "..type(pathOrFiles)..")", 3) -- luacheck: ignore + error("bad argument #1 to 'load' (string/table of strings expected, got "..type(pathOrFiles)..")", 3) end if (type(pathOrFiles) == "string") then local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore if (info == nil or info.type ~= "directory") then - error("bad argument #1 to 'load' (path '"..pathOrFiles.."' not found)", 3) -- luacheck: ignore + error("bad argument #1 to 'load' (path '"..pathOrFiles.."' not found)", 3) end local files = love.filesystem.getDirectoryItems(pathOrFiles) @@ -82,18 +82,34 @@ local function load(pathOrFiles, namespace) end 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" +--@see Components function Concord.loadComponents(pathOrFiles) load(pathOrFiles, 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" +--@see Systems function Concord.loadSystems(pathOrFiles) load(pathOrFiles, Concord.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" +--@see Worlds function Concord.loadWorlds(pathOrFiles) load(pathOrFiles, Concord.worlds) 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" +--@see Assemblages function Concord.loadAssemblages(pathOrFiles) load(pathOrFiles, Concord.assemblages) end diff --git a/src/list.lua b/src/list.lua index 94fee52..38ff949 100644 --- a/src/list.lua +++ b/src/list.lua @@ -1,4 +1,5 @@ --- List +-- Data structure that allows for fast removal at the cost of containing order. local List = {} List.__mt = { @@ -6,7 +7,7 @@ List.__mt = { } --- Creates a new List. --- @return A new list +-- @return A new List function List.new() return setmetatable({ size = 0, @@ -14,9 +15,11 @@ function List.new() end --- Adds an object to the List. --- @param obj The object to add +-- Object must be of reference type +-- Object may not be the string 'size' +-- @param obj Object to add -- @return self -function List:__add(obj) -- obj can not be a number and also not the string "size" +function List:__add(obj) local size = self.size + 1 self[size] = obj @@ -27,7 +30,7 @@ function List:__add(obj) -- obj can not be a number and also not the string "siz end --- Removes an object from the List. --- @param obj The object to remove +-- @param obj Object to remove -- @return self function List:__remove(obj) local index = self[obj] @@ -66,17 +69,23 @@ function List:__clear() return self end ---- Gets if the List has the object. --- @param obj The object to search for --- true if the list has the object, false otherwise +--- Returns true if the List has the object. +-- @param obj Object to check for +-- @return True if the List has the object, false otherwise function List:has(obj) return self[obj] and true or false end +--- Returns the object at an index. +-- @param i Index to get from +-- @return Object at the index function List:get(i) return self[i] end +--- Returns the index of an object in the List. +-- @param obj Object to get index of +-- @return index of object in the List. function List:indexOf(obj) if (not self[obj]) then error("bad argument #1 to 'List:indexOf' (Object was not in List)", 2) diff --git a/src/pool.lua b/src/pool.lua index 1b7be39..4cde567 100644 --- a/src/pool.lua +++ b/src/pool.lua @@ -1,4 +1,6 @@ --- Pool +-- A Pool is used to iterate over Entities with a specific Components +-- A Pool contain a any amount of Entities. local PATH = (...):gsub('%.[^%.]+$', '') @@ -10,8 +12,8 @@ Pool.__mt = { } --- Creates a new Pool --- @param name Identifier for the Pool. --- @param filter Table containing the required Components +-- @param name Name for the Pool. +-- @param filter Table containing the required BaseComponents -- @return The new Pool function Pool.new(name, filter) local pool = setmetatable(List(), Pool.__mt) @@ -25,7 +27,7 @@ function Pool.new(name, filter) end --- Checks if an Entity is eligible for the Pool. --- @param e The Entity to check +-- @param e Entity to check -- @return True if the entity is eligible, false otherwise function Pool:__eligible(e) for _, component in ipairs(self.__filter) do @@ -37,27 +39,46 @@ function Pool:__eligible(e) return true end +--- Internal: Adds an Entity to the Pool. +-- @param e Entity to add +-- @return self function Pool:__add(e) List.__add(self, e) self:onEntityAdded(e) + + return self end +--- Internal: Removed an Entity from the Pool. +-- @param e Entity to remove +-- @return self function Pool:__remove(e) List.__remove(self, e) self:onEntityRemoved(e) + + return self end +--- Gets the name of the Pool +-- @return Name of the Pool. 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. +-- @param e Entity that was added. function Pool:onEntityAdded(e) -- luacheck: ignore end +-- Callback for when an Entity is removed from the Pool. +-- @param e Entity that was removed. function Pool:onEntityRemoved(e) -- luacheck: ignore end diff --git a/src/system.lua b/src/system.lua index 4e0b778..5c12d39 100644 --- a/src/system.lua +++ b/src/system.lua @@ -1,4 +1,7 @@ --- System +-- A System iterates over Entities. From these Entities its get Components and modify them. +-- A System contains 1 or more Pools. +-- A System is contained by 1 World. local PATH = (...):gsub('%.[^%.]+$', '') @@ -11,7 +14,7 @@ local System = { System.mt = { __index = System, - __call = function(baseSystem, world) + __call = function(systemClass, world) local system = setmetatable({ __enabled = true, @@ -19,8 +22,8 @@ System.mt = { __world = world, __isSystem = true, - __isBaseSystem = false, -- Overwrite value from baseSystem - }, baseSystem) + __isSystemClass = false, -- Overwrite value from systemClass + }, systemClass) -- Optimization: We deep copy the World class into our instance of a world. -- This grants slightly faster access times at the cost of memory. @@ -29,8 +32,8 @@ System.mt = { Utils.shallowCopy(System, system) end - for _, filter in pairs(baseSystem.__filter) do - local pool = system:__buildPool(filter) + 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 @@ -45,39 +48,40 @@ System.mt = { end, } ---- Creates a new System prototype. +--- Creates a new SystemClass. -- @param ... Variable amounts of filters --- @return A new System prototype +-- @return A new SystemClass function System.new(...) - local baseSystem = setmetatable({ - __isBaseSystem = true, + local systemClass = setmetatable({ + __isSystemClass = true, __filter = {...}, }, System.mt) - baseSystem.__index = baseSystem + systemClass.__index = systemClass - return baseSystem + return systemClass end ---- Builds a Pool for the System. +--- Internal: Builds a Pool for the System. -- @param baseFilter The 'raw' Filter -- @return A new Pool -function System:__buildPool(baseFilter) -- luacheck: ignore +function System.__buildPool(baseFilter) local name = "pool" local filter = {} - for _, v in ipairs(baseFilter) do - if type(v) == "table" then - filter[#filter + 1] = v - elseif type(v) == "string" then - name = v + 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 ---- Checks and applies an Entity to the System's pools. +--- Internal: Evaluates an Entity for all the System's Pools. -- @param e The Entity to check +-- @return self function System:__evaluate(e) for _, pool in ipairs(self.__pools) do local has = pool:has(e) @@ -93,8 +97,9 @@ function System:__evaluate(e) return self end ---- Remove an Entity from the System. +--- Internal: Removes an Entity from the System. -- @param e The Entity to remove +-- @return self function System:__remove(e) for _, pool in ipairs(self.__pools) do if pool:has(e) then @@ -105,6 +110,8 @@ function System:__remove(e) return self end +--- Internal: Clears all Entities from the System. +-- @return self function System:clear() for i = 1, #self.__pools do self.__pools[i]:__clear() @@ -113,59 +120,68 @@ function System:clear() return self end +--- Enables the System. +-- @return self function System:enable() self:setEnabled(true) return self end +--- Disables the System. +-- @return self function System:disable() self:setEnabled(false) return self end +--- Toggles if the System is enabled. +-- @return self function System:toggleEnable() self:setEnabled(not self.__enabled) return self end +--- Sets if the System is enabled +-- @param enable Enable +-- @return self function System:setEnabled(enable) if (not self.__enabled and enable) then self.__enabled = true - self:onEnabledCallback() + self:onEnabled() elseif (self.__enabled and not enable) then self.__enabled = false - self:onDisabledCallback() + self:onDisabled() end return self end +--- Returns is the System is enabled +-- @return True if the System is enabled, false otherwise function System:isEnabled() return self.__enabled end --- Returns the World the System is in. --- @return The world the system is in +-- @return The World the System is in function System:getWorld() return self.__world end ---- Default callback for system initialization. +--- Callback for system initialization. -- @param world The World the System was added to function System:init(world) -- luacheck: ignore end --- Default callback for when a System's callback is enabled. --- @param callbackName The name of the callback that was enabled -function System:onEnabledCallback(callbackName) -- luacheck: ignore +-- Callback for when a System is enabled. +function System:onEnabled() -- luacheck: ignore end --- Default callback for when a System's callback is disabled. --- @param callbackName The name of the callback that was disabled -function System:onDisabledCallback(callbackName) -- luacheck: ignore +-- Callback for when a System is disabled. +function System:onDisabled() -- luacheck: ignore end return setmetatable(System, { diff --git a/src/systems.lua b/src/systems.lua index 4f84472..81e6b2e 100644 --- a/src/systems.lua +++ b/src/systems.lua @@ -1,4 +1,5 @@ --- Systems +--- Systems +-- Container for registered SystemClasses local PATH = (...):gsub('%.[^%.]+$', '') @@ -6,20 +7,23 @@ local Type = require(PATH..".type") local Systems = {} -function Systems.register(name, system) +--- Registers a SystemClass. +-- @param name Name to register under +-- @param 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.isBaseSystem(system)) then - error("bad argument #2 to 'Systems.register' (baseSystem expected, got "..type(system)..")", 3) + 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] = system + Systems[name] = systemClass end return setmetatable(Systems, { diff --git a/src/type.lua b/src/type.lua index a7ba264..9a882a8 100644 --- a/src/type.lua +++ b/src/type.lua @@ -1,31 +1,53 @@ --- Type +--- Type +-- Helper module to do easy type checking for Concord types local Type = {} +--- Returns if object is an Entity. +-- @param t Object to check +-- @return True if object is an Entity, false otherwise function Type.isEntity(t) return type(t) == "table" and t.__isEntity or false end -function Type.isBaseComponent(t) - return type(t) == "table" and t.__isBaseComponent or false +--- Returns if object is a ComponentClass. +-- @param t Object to check +-- @return True if object is an ComponentClass, false otherwise +function Type.isComponentClass(t) + return type(t) == "table" and t.__isComponentClass or false end +--- Returns if object is a Component. +-- @param t Object to check +-- @return True if object is an Component, false otherwise function Type.isComponent(t) return type(t) == "table" and t.__isComponent or false end -function Type.isBaseSystem(t) - return type(t) == "table" and t.__isBaseSystem or false +--- Returns if object is a SystemClass. +-- @param t Object to check +-- @return True if object is an SystemClass, false otherwise +function Type.isSystemClass(t) + return type(t) == "table" and t.__isSystemClass or false end +--- Returns if object is a System. +-- @param t Object to check +-- @return True if object is an System, false otherwise function Type.isSystem(t) return type(t) == "table" and t.__isSystem or false end +--- Returns if object is a World. +-- @param t Object to check +-- @return True if object is an World, false otherwise 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 +-- @return True if object is an Assemblage, false otherwise function Type.isAssemblage(t) return type(t) == "table" and t.__isAssemblage or false end diff --git a/src/utils.lua b/src/utils.lua index 43adf6c..6372b5b 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -1,5 +1,11 @@ +--- Utils +-- Helper module for misc operations + local Utils = {} +--- Does a shallow copy of a table and appends it to a target table. +-- @param orig Table to copy +-- @param target Table to append to function Utils.shallowCopy(orig, target) for key, value in pairs(orig) do target[key] = value diff --git a/src/world.lua b/src/world.lua index f15a14d..a8c7953 100644 --- a/src/world.lua +++ b/src/world.lua @@ -1,4 +1,8 @@ --- World +-- A World is a collection of Systems and Entities +-- A world emits to let Systems iterate +-- A World contains any amount of Systems +-- A World contains any amount of Entities local PATH = (...):gsub('%.[^%.]+$', '') @@ -42,7 +46,7 @@ function World.new() end --- Adds an Entity to the World. --- @param e The Entity to add +-- @param e Entity to add -- @return self function World:addEntity(e) if not Type.isEntity(e) then @@ -59,8 +63,8 @@ function World:addEntity(e) return self end ---- Removes an entity from the World. --- @param e The Entity to mark +--- Removes an Entity from the World. +-- @param e Entity to remove -- @return self function World:removeEntity(e) if not Type.isEntity(e) then @@ -72,13 +76,18 @@ function World:removeEntity(e) return self end +--- Internal: Marks an Entity as dirty. +-- @see Entity:__dirty +-- @param e Entity to mark as dirty function World:__dirtyEntity(e) if not self.__dirty:has(e) then self.__dirty:__add(e) end end - +--- Internal: Flushes all changes to Entities. +-- This processes all entities. Adding and removing entities, as well as reevaluating dirty entities. +-- @see System:__evaluate -- @return self function World:__flush() -- Early out @@ -135,26 +144,37 @@ function World:__flush() return self end -local blacklistedSystemMethods = { +-- These functions won't be seen as callbacks that will be emitted to. +local blacklistedSystemFunctions = { "init", + "onEnabled", + "onDisabled", } -function World:addSystem(baseSystem) - if (not Type.isBaseSystem(baseSystem)) then - error("bad argument #1 to 'World:addSystems' (baseSystem expected, got "..type(baseSystem)..")", 2) +--- Adds a System to the World. +-- Callbacks are registered automatically +-- Entities added before are added to the System retroactively +-- @see World:emit +-- @param systemClass SystemClass of System to add +-- @return self +function World:addSystem(systemClass) + if (not Type.isSystemClass(systemClass)) then + error("bad argument #1 to 'World:addSystems' (SystemClass expected, got "..type(systemClass)..")", 2) end - -- TODO: Check if baseSystem was already added + if (self.__systemLookup[systemClass]) then + error("bad argument #1 to 'World:addSystems' (SystemClass was already added to World)", 2) + end -- Create instance of system - local system = baseSystem(self) + local system = systemClass(self) - self.__systemLookup[baseSystem] = system + self.__systemLookup[systemClass] = system self.systems:__add(system) - for callbackName, callback in pairs(baseSystem) do + for callbackName, callback in pairs(systemClass) do -- Skip callback if its blacklisted - if (not blacklistedSystemMethods[callbackName]) then + if (not blacklistedSystemFunctions[callbackName]) then -- Make container for all listeners of the callback if it does not exist yet if (not self.events[callbackName]) then self.events[callbackName] = {} @@ -173,43 +193,59 @@ function World:addSystem(baseSystem) for j = 1, self.entities.size do system:__evaluate(self.entities[j]) end + + return self end +--- Adds multiple Systems to the World. +-- Callbacks are registered automatically +-- @see World:addSystem +-- @see World:emit +-- @param ... SystemClasses of Systems to add +-- @return self function World:addSystems(...) for i = 1, select("#", ...) do - local baseSystem = select(i, ...) + local systemClass = select(i, ...) - self:addSystem(baseSystem) - end -end - - -function World:hasSystem(baseSystem) - if not Type.isBaseSystem(baseSystem) then - error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) + self:addSystem(systemClass) end - return self.__systemLookup[baseSystem] and true or false + return self end -function World:getSystem(baseSystem) - if not Type.isBaseSystem(baseSystem) then - error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2) +--- Returns if the World has a System. +-- @param systemClass SystemClass of System to check for +-- @return True if World has System, false otherwise +function World:hasSystem(systemClass) + if not Type.isSystemClass(systemClass) then + error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) end - return self.__systemLookup[baseSystem] + return self.__systemLookup[systemClass] and true or false end ---- Emits an Event in the World. --- @param eventName The Event that should be emitted --- @param ... Parameters passed to listeners +--- Gets a System from the World. +-- @param systemClass SystemClass of System to get +-- @return System to get +function World:getSystem(systemClass) + if not Type.isSystemClass(systemClass) then + error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) + end + + return self.__systemLookup[systemClass] +end + +--- Emits a callback in the World. +-- Calls all functions with the functionName of added Systems +-- @param functionName Name of functions to call. +-- @param ... Parameters passed to System's functions -- @return self -function World:emit(callbackName, ...) - if not callbackName or type(callbackName) ~= "string" then - error("bad argument #1 to 'World:emit' (String expected, got "..type(callbackName)..")") +function World:emit(functionName, ...) + if not functionName or type(functionName) ~= "string" then + error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") end - local listeners = self.events[callbackName] + local listeners = self.events[functionName] if listeners then for i = 1, #listeners do @@ -233,15 +269,19 @@ function World:clear() self:removeEntity(self.entities[i]) end + for i = 1, self.systems.size do + self.systems[i]:clear() + end + return self end ---- Default callback for adding an Entity. +--- Callback for when an Entity is added to the World. -- @param e The Entity that was added function World:onEntityAdded(e) -- luacheck: ignore end ---- Default callback for removing an Entity. +--- Callback for when an Entity is removed from the World. -- @param e The Entity that was removed function World:onEntityRemoved(e) -- luacheck: ignore end diff --git a/src/worlds.lua b/src/worlds.lua index 2f8f5a8..0ec2574 100644 --- a/src/worlds.lua +++ b/src/worlds.lua @@ -1,4 +1,5 @@ --- Worlds +--- Worlds +-- Container for registered Worlds local PATH = (...):gsub('%.[^%.]+$', '') @@ -6,6 +7,9 @@ local Type = require(PATH..".type") local Worlds = {} +--- Registers a World. +-- @param 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) From 368766bd27d6c5fefea04ae4a8f64636dfe2af4e Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 01:41:58 +0100 Subject: [PATCH 038/113] Fix broken LDoc tags --- config.ld | 2 +- src/assemblage.lua | 1 - src/components.lua | 2 +- src/entity.lua | 1 - src/init.lua | 4 ---- src/world.lua | 2 -- 6 files changed, 2 insertions(+), 10 deletions(-) diff --git a/config.ld b/config.ld index aada8c2..a04aeaf 100644 --- a/config.ld +++ b/config.ld @@ -1,3 +1,3 @@ project = 'Concord' description = 'A feature-complete ECS library' -file = {'lib', exclude = {'lib/type.lua', 'lib/run.lua'}} \ No newline at end of file +file = {'src', exclude = {}} \ No newline at end of file diff --git a/src/assemblage.lua b/src/assemblage.lua index c2303f2..f989a63 100644 --- a/src/assemblage.lua +++ b/src/assemblage.lua @@ -21,7 +21,6 @@ function Assemblage.new(assemble) end --- Assembles an Entity. --- @see Entity:assemble -- @param e Entity to assemble -- @param ... Varargs to pass to the assemble function -- @ return self diff --git a/src/components.lua b/src/components.lua index 76780de..ba88c7e 100644 --- a/src/components.lua +++ b/src/components.lua @@ -1,4 +1,4 @@ --- Components +--- Components -- Container for registered ComponentClasss local PATH = (...):gsub('%.[^%.]+$', '') diff --git a/src/entity.lua b/src/entity.lua index c084039..cce5213 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -98,7 +98,6 @@ function Entity:remove(componentClass) end --- Assembles an Entity. --- @see Assemblage:assemble -- @param assemblage Assemblage to assemble with -- @param ... Varargs to pass to the Assemblage's assemble function. function Entity:assemble(assemblage, ...) diff --git a/src/init.lua b/src/init.lua index b373989..1f65695 100644 --- a/src/init.lua +++ b/src/init.lua @@ -85,7 +85,6 @@ 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" ---@see Components function Concord.loadComponents(pathOrFiles) load(pathOrFiles, Concord.components) end @@ -93,7 +92,6 @@ 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" ---@see Systems function Concord.loadSystems(pathOrFiles) load(pathOrFiles, Concord.systems) end @@ -101,7 +99,6 @@ 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" ---@see Worlds function Concord.loadWorlds(pathOrFiles) load(pathOrFiles, Concord.worlds) end @@ -109,7 +106,6 @@ 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" ---@see Assemblages function Concord.loadAssemblages(pathOrFiles) load(pathOrFiles, Concord.assemblages) end diff --git a/src/world.lua b/src/world.lua index a8c7953..fe6107e 100644 --- a/src/world.lua +++ b/src/world.lua @@ -77,7 +77,6 @@ function World:removeEntity(e) end --- Internal: Marks an Entity as dirty. --- @see Entity:__dirty -- @param e Entity to mark as dirty function World:__dirtyEntity(e) if not self.__dirty:has(e) then @@ -87,7 +86,6 @@ end --- Internal: Flushes all changes to Entities. -- This processes all entities. Adding and removing entities, as well as reevaluating dirty entities. --- @see System:__evaluate -- @return self function World:__flush() -- Early out From 58d9e44bb17ee4291429402ce1991d50e92bcec2 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 01:52:01 +0100 Subject: [PATCH 039/113] Remove test files --- conf.lua | 10 ------ main.lua | 56 --------------------------------- test/components/test_comp_1.lua | 6 ---- 3 files changed, 72 deletions(-) delete mode 100644 conf.lua delete mode 100644 main.lua delete mode 100644 test/components/test_comp_1.lua diff --git a/conf.lua b/conf.lua deleted file mode 100644 index 4a1ab1c..0000000 --- a/conf.lua +++ /dev/null @@ -1,10 +0,0 @@ -function love.conf(t) - t.identity = "Concord Example" - t.version = "11.0" - t.console = true - - t.window.vsync = false - t.window.width = 720 - t.window.height = 720 - end - \ No newline at end of file diff --git a/main.lua b/main.lua deleted file mode 100644 index ff2ec47..0000000 --- a/main.lua +++ /dev/null @@ -1,56 +0,0 @@ ---[=[ -local file = "examples.simpleDrawing" --- local file = "examples.baseLayout.main" - -require(file) -]=]-- - ---test - -local Concord = require("src") - -local Component = Concord.component -local System = Concord.system -local Entity = Concord.entity -local World = Concord.world - -local test_comp_1 = Component(function(e, a) - e.a = a -end) - -local test_system_1 = System({test_comp_1}) -local test_system_2 = System({test_comp_1}) -local test_system_3 = System({test_comp_1}) - -function test_system_1:test() - for _, _ in ipairs(self.pool) do - end -end - -function test_system_2:test() - for _, _ in ipairs(self.pool) do - end -end - -function test_system_3:test() - for _, _ in ipairs(self.pool) do - end -end - -local world = World() - -world:addSystems(test_system_1, test_system_2, test_system_3) - -for _ = 1, 100 do - local entity = Entity(world) - entity:give(test_comp_1, 100, 100) -end - - -local start = love.timer.getTime() -for _ = 1, 1000000 do - world:emit("test") -end -local stop = love.timer.getTime() - -print("Time taken: " .. stop - start) \ No newline at end of file diff --git a/test/components/test_comp_1.lua b/test/components/test_comp_1.lua deleted file mode 100644 index ef9b98a..0000000 --- a/test/components/test_comp_1.lua +++ /dev/null @@ -1,6 +0,0 @@ -local Component = require("src").component - -return Component(function(e, x, y) - e.x = x - e.y = y -end) \ No newline at end of file From 0e1023d2cebb9b9ebeaa56039237fb2c815310ba Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 01:54:21 +0100 Subject: [PATCH 040/113] Publish regenerated LDoc pages --- docs/index.html | 74 +++++- docs/modules/assemblage.html | 147 +++++++++++ docs/modules/assemblages.html | 116 +++++++++ docs/modules/component.html | 51 +++- docs/modules/components.html | 116 +++++++++ docs/modules/entity.html | 247 ++++++++++++++---- docs/modules/init.html | 110 ++++++-- docs/modules/instance.html | 463 ---------------------------------- docs/modules/list.html | 153 +++++++---- docs/modules/pool.html | 156 +++++++++++- docs/modules/system.html | 362 +++++++++++++------------- docs/modules/systems.html | 116 +++++++++ docs/modules/type.html | 292 +++++++++++++++++++++ docs/modules/utils.html | 116 +++++++++ docs/modules/world.html | 453 +++++++++++++++++++++++++++++++++ docs/modules/worlds.html | 116 +++++++++ 16 files changed, 2283 insertions(+), 805 deletions(-) create mode 100644 docs/modules/assemblage.html create mode 100644 docs/modules/assemblages.html create mode 100644 docs/modules/components.html delete mode 100644 docs/modules/instance.html create mode 100644 docs/modules/systems.html create mode 100644 docs/modules/type.html create mode 100644 docs/modules/utils.html create mode 100644 docs/modules/world.html create mode 100644 docs/modules/worlds.html diff --git a/docs/index.html b/docs/index.html index 746fc48..c46ade3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,13 +31,20 @@

Modules

@@ -49,33 +56,78 @@

Modules

+ + + + + + + + - + + + + + - + - - - - - + - + - + + + + + + + + + + + + + + + + + + + + +
assemblageAssemblage + An Assemblage is a function that 'makes' an entity something.
assemblagesAssemblages + Container for registered Assemblages
componentComponentComponent + A Component is a pure data container.
componentsComponents + Container for registered ComponentClasss
entityEntityEntity + Entities are the concrete objects that exist in your project.
init init
instanceInstance
listListList + Data structure that allows for fast removal at the cost of containing order.
poolPoolPool + A Pool is used to iterate over Entities with a specific Components + A Pool contain a any amount of Entities.
systemSystemSystem + A System iterates over Entities.
systemsSystems + Container for registered SystemClasses
typeType + Helper module to do easy type checking for Concord types
utilsUtils + Helper module for misc operations
worldWorld + A World is a collection of Systems and Entities + A world emits to let Systems iterate + A World contains any amount of Systems + A World contains any amount of Entities
worldsWorlds + Container for registered Worlds
@@ -83,7 +135,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/assemblage.html b/docs/modules/assemblage.html new file mode 100644 index 0000000..bfb4d07 --- /dev/null +++ b/docs/modules/assemblage.html @@ -0,0 +1,147 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module assemblage

+

Assemblage + An Assemblage is a function that 'makes' an entity something.

+

+ It does this by :give'ing or :ensure'ing Components, or by :assemble'ing the Entity.

+ + +

Functions

+ + + + + + + + + +
Assemblage.new (assemble)Creates a new Assemblage.
Assemblage:assemble (e, ...)Assembles an Entity.
+ +
+
+ + +

Functions

+ +
+
+ + Assemblage.new (assemble) +
+
+ Creates a new Assemblage. + + +

Parameters:

+
    +
  • assemble + Function that assembles an Entity +
  • +
+ +

Returns:

+
    + + A new Assemblage +
+ + + + +
+
+ + Assemblage:assemble (e, ...) +
+
+ Assembles an Entity. + + +

Parameters:

+
    +
  • e + Entity to assemble +
  • +
  • ... + Varargs to pass to the assemble function + @ return self +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/assemblages.html b/docs/modules/assemblages.html new file mode 100644 index 0000000..4aeed9c --- /dev/null +++ b/docs/modules/assemblages.html @@ -0,0 +1,116 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module assemblages

+

Assemblages + Container for registered Assemblages

+

+ + +

Functions

+ + + + + +
Assemblages.register (name, assemblage)Registers an Assemblage.
+ +
+
+ + +

Functions

+ +
+
+ + Assemblages.register (name, assemblage) +
+
+ Registers an Assemblage. + + +

Parameters:

+
    +
  • name + Name to register under +
  • +
  • assemblage + Assemblage to register +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/component.html b/docs/modules/component.html index 37f2162..a5ce1fb 100644 --- a/docs/modules/component.html +++ b/docs/modules/component.html @@ -38,13 +38,20 @@

Modules

@@ -52,19 +59,25 @@

Module component

-

Component

-

+

Component + A Component is a pure data container.

+

+ A Component is contained by a single entity.

Functions

- + + + + + - +
Component.new (populate)Creates a new Component.Creates a new ComponentClass.
Component:__populate ()Internal: Populates a Component with values
Component:__initialize (...)Creates and initializes a new Bag.Internal: Creates and populates a new Component.
@@ -80,45 +93,59 @@ Component.new (populate)
- Creates a new Component. + Creates a new ComponentClass.

Parameters:

  • populate - A function that populates the Bag with values + Function that populates a Component with values

Returns:

    - A Component object + A new ComponentClass
+
+
+ + Component:__populate () +
+
+ Internal: Populates a Component with values + + + + + + +
Component:__initialize (...)
- Creates and initializes a new Bag. + Internal: Creates and populates a new Component.

Parameters:

  • ... - The values passed to the populate function + Varargs passed to the populate function

Returns:

    - A new initialized Bag + A new populated Component
@@ -132,7 +159,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/components.html b/docs/modules/components.html new file mode 100644 index 0000000..c9e4dd7 --- /dev/null +++ b/docs/modules/components.html @@ -0,0 +1,116 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module components

+

Components + Container for registered ComponentClasss

+

+ + +

Functions

+ + + + + +
Components.register (name, componentClass)Registers a ComponentClass.
+ +
+
+ + +

Functions

+ +
+
+ + Components.register (name, componentClass) +
+
+ Registers a ComponentClass. + + +

Parameters:

+
    +
  • name + Name to register under +
  • +
  • componentClass + ComponentClass to register +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/entity.html b/docs/modules/entity.html index 34281f0..4350a6f 100644 --- a/docs/modules/entity.html +++ b/docs/modules/entity.html @@ -38,13 +38,20 @@

Modules

@@ -52,39 +59,62 @@

Module entity

-

Entity

-

+

Entity + Entities are the concrete objects that exist in your project.

+

+ An Entity have Components and are processed by Systems. + An Entity is contained by a maximum of 1 World.

Functions

- - + + - - + + - - + + - - + + + + + + - + + + + + + + + + - - + + + + + + + + + +
Entity.new ()Creates and initializes a new Entity.Entity.new (world)Creates a new Entity.
Entity:give (component, ...)Gives an Entity a component with values.Entity:give (componentClass, ...)Gives an Entity a Component.
Entity:remove (component)Removes a component from an Entity.Entity:ensure (componentClass, ...)Ensures an Entity to have a Component.
Entity:apply ()Checks the Entity against the pools again.Entity:remove (componentClass)Removes a Component from an Entity.
Entity:assemble (assemblage, ...)Assembles an Entity.
Entity:destroy () Destroys the Entity.
Entity:get (component)Entity:__dirty ()Internal: Tells the World it's in that this Entity is dirty.
Entity:has (componentClass)Returns true if the Entity has a Component.
Entity:get (componentClass) Gets a Component from the Entity.
Entity:has (component)Returns true if the Entity has the Component.Entity:getComponents ()Returns a table of all Components the Entity has.
Entity:inWorld ()Returns true if the Entity is in a World.
Entity:getWorld ()Returns the World the Entity is in.
@@ -97,12 +127,18 @@
- Entity.new () + Entity.new (world)
- Creates and initializes a new Entity. + Creates a new Entity. Optionally adds it to a World. +

Parameters:

+
    +
  • world + Optional World to add the entity to +
  • +

Returns:

    @@ -116,19 +152,49 @@
- Entity:give (component, ...) + Entity:give (componentClass, ...)
- Gives an Entity a component with values. + Gives an Entity a Component. + If the Component already exists, it's overridden by this new Component

Parameters:

    -
  • component - The Component to add +
  • componentClass + ComponentClass to add an instance of
  • ... - The values passed to the Component + varargs passed to the Component's populate function +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + Entity:ensure (componentClass, ...) +
+
+ Ensures an Entity to have a Component. + If the Component already exists, no action is taken + + +

Parameters:

+
    +
  • componentClass + ComponentClass to add an instance of +
  • +
  • ... + varargs passed to the Component's populate function
@@ -144,16 +210,16 @@
- Entity:remove (component) + Entity:remove (componentClass)
- Removes a component from an Entity. + Removes a Component from an Entity.

Parameters:

    -
  • component - The Component to remove +
  • componentClass + ComponentClass of the Component to remove
@@ -168,19 +234,23 @@
- - Entity:apply () + + Entity:assemble (assemblage, ...)
- Checks the Entity against the pools again. + Assembles an Entity. +

Parameters:

+
    +
  • assemblage + Assemblage to assemble with +
  • +
  • ... + Varargs to pass to the Assemblage's assemble function. +
  • +
-

Returns:

-
    - - self -
@@ -192,6 +262,7 @@
Destroys the Entity. + Removes the Entity from it's World if it's in one. @@ -206,24 +277,18 @@
- - Entity:get (component) + + Entity:__dirty ()
- Gets a Component from the Entity. + Internal: Tells the World it's in that this Entity is dirty. -

Parameters:

-
    -
  • component - The Component to get -
  • -

Returns:

    - The Bag from the Component + self
@@ -232,23 +297,107 @@
- Entity:has (component) + Entity:has (componentClass)
- Returns true if the Entity has the Component. + Returns true if the Entity has a Component.

Parameters:

    -
  • component - The Component to check against +
  • componentClass + ComponentClass of the Component to check

Returns:

    - True if the entity has the Bag. False otherwise + True if the Entity has the Component, false otherwise +
+ + + + +
+
+ + Entity:get (componentClass) +
+
+ Gets a Component from the Entity. + + +

Parameters:

+
    +
  • componentClass + ComponentClass of the Component to get +
  • +
+ +

Returns:

+
    + + The Component +
+ + + + +
+
+ + Entity:getComponents () +
+
+ Returns a table of all Components the Entity has. + Warning: Do not modify this table. + Use Entity:give/ensure/remove instead + + + +

Returns:

+
    + + Table of all Components the Entity has +
+ + + + +
+
+ + Entity:inWorld () +
+
+ Returns true if the Entity is in a World. + + + +

Returns:

+
    + + True if the Entity is in a World, false otherwise +
+ + + + +
+
+ + Entity:getWorld () +
+
+ Returns the World the Entity is in. + + + +

Returns:

+
    + + The World the Entity is in.
@@ -262,7 +411,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/init.html b/docs/modules/init.html index 163ad9e..b3b246d 100644 --- a/docs/modules/init.html +++ b/docs/modules/init.html @@ -38,13 +38,20 @@

Modules

@@ -59,8 +66,20 @@

Functions

- - + + + + + + + + + + + + + +
Concord.init (settings)Initializes the library with some optional settingsConcord.loadComponents (pathOrFiles)Loads ComponentClasses and puts them in the Components container.
Concord.loadSystems (pathOrFiles)Loads SystemClasses and puts them in the Systems container.
Concord.loadWorlds (pathOrFiles)Loads Worlds and puts them in the Worlds container.
Concord.loadAssemblages (pathOrFiles)Loads Assemblages and puts them in the Assemblages container.
@@ -72,27 +91,88 @@
- - Concord.init (settings) + + Concord.loadComponents (pathOrFiles)
- Initializes the library with some optional settings + 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"

Parameters:

    -
  • settings - Table of settings: { - useEvents Flag to overwrite love.run and use events. Defaults to false - } +
  • pathOrFiles +
-

Returns:

-
    - Concord -
+ + + +
+
+ + Concord.loadSystems (pathOrFiles) +
+
+ 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" + + +

Parameters:

+
    +
  • pathOrFiles + +
  • +
+ + + + + +
+
+ + Concord.loadWorlds (pathOrFiles) +
+
+ 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" + + +

Parameters:

+
    +
  • pathOrFiles + +
  • +
+ + + + + +
+
+ + Concord.loadAssemblages (pathOrFiles) +
+
+ 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" + + +

Parameters:

+
    +
  • pathOrFiles + +
  • +
+ @@ -105,7 +185,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/instance.html b/docs/modules/instance.html deleted file mode 100644 index d79a6c5..0000000 --- a/docs/modules/instance.html +++ /dev/null @@ -1,463 +0,0 @@ - - - - - Reference - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Module instance

-

Instance

-

- - -

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Instance.new ()Creates a new Instance.
Instance:addEntity (e)Adds an Entity to the Instance.
Instance:checkEntity (e)Checks an Entity against all the systems in the Instance.
Instance:removeEntity (e)Marks an Entity as removed from the Instance.
Instance:flush ()Completely removes all marked Entities in the Instance.
Instance:addSystem (system, eventName, callback, enabled)Adds a System to the Instance.
Instance:enableSystem (system, eventName, callback)Enables a System in the Instance.
Instance:disableSystem (system, eventName, callback)Disables a System in the Instance.
Instance:setSystem (system, eventName, callback, enable)Sets a System 'enable' in the Instance.
Instance:emit (eventName, ...)Emits an Event in the Instance.
Instance:clear ()Removes all entities from the Instance
Instance:onEntityAdded (e)Default callback for adding an Entity.
Instance:onEntityRemoved (e)Default callback for removing an Entity.
- -
-
- - -

Functions

- -
-
- - Instance.new () -
-
- Creates a new Instance. - - - -

Returns:

-
    - - The new instance -
- - - - -
-
- - Instance:addEntity (e) -
-
- Adds an Entity to the Instance. - - -

Parameters:

-
    -
  • e - The Entity to add -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:checkEntity (e) -
-
- Checks an Entity against all the systems in the Instance. - - -

Parameters:

-
    -
  • e - The Entity to check -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:removeEntity (e) -
-
- Marks an Entity as removed from the Instance. - - -

Parameters:

-
    -
  • e - The Entity to mark -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:flush () -
-
- Completely removes all marked Entities in the Instance. - - - -

Returns:

-
    - - self -
- - - - -
-
- - Instance:addSystem (system, eventName, callback, enabled) -
-
- Adds a System to the Instance. - - -

Parameters:

-
    -
  • system - The System to add -
  • -
  • eventName - The Event to register to -
  • -
  • callback - The function name to call. Defaults to eventName -
  • -
  • enabled - If the system is enabled. Defaults to true -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:enableSystem (system, eventName, callback) -
-
- Enables a System in the Instance. - - -

Parameters:

-
    -
  • system - The System to enable -
  • -
  • eventName - The Event it was registered to -
  • -
  • callback - The callback it was registered with. Defaults to eventName -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:disableSystem (system, eventName, callback) -
-
- Disables a System in the Instance. - - -

Parameters:

-
    -
  • system - The System to disable -
  • -
  • eventName - The Event it was registered to -
  • -
  • callback - The callback it was registered with. Defaults to eventName -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:setSystem (system, eventName, callback, enable) -
-
- Sets a System 'enable' in the Instance. - - -

Parameters:

-
    -
  • system - The System to set -
  • -
  • eventName - The Event it was registered to -
  • -
  • callback - The callback it was registered with. Defaults to eventName -
  • -
  • enable - The state to set it to -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:emit (eventName, ...) -
-
- Emits an Event in the Instance. - - -

Parameters:

-
    -
  • eventName - The Event that should be emitted -
  • -
  • ... - Parameters passed to listeners -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Instance:clear () -
-
- Removes all entities from the Instance - - - -

Returns:

-
    - - self -
- - - - -
-
- - Instance:onEntityAdded (e) -
-
- Default callback for adding an Entity. - - -

Parameters:

-
    -
  • e - The Entity that was added -
  • -
- - - - - -
-
- - Instance:onEntityRemoved (e) -
-
- Default callback for removing an Entity. - - -

Parameters:

-
    -
  • e - The Entity that was removed -
  • -
- - - - - -
-
- - -
-
-
-generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 -
-
- - diff --git a/docs/modules/list.html b/docs/modules/list.html index 8d384f5..757c1d3 100644 --- a/docs/modules/list.html +++ b/docs/modules/list.html @@ -38,13 +38,20 @@

Modules

@@ -52,7 +59,8 @@

Module list

-

List

+

List + Data structure that allows for fast removal at the cost of containing order.

@@ -63,24 +71,28 @@ Creates a new List. - List:clear () - Clears the List completely. - - - List:add (obj) + List:__add (obj) Adds an object to the List. - List:remove (obj) + List:__remove (obj) Removes an object from the List. - List:get (index) - Gets an object by numerical index. + List:__clear () + Clears the List completely. List:has (obj) - Gets if the List has the object. + Returns true if the List has the object. + + + List:get (i) + Returns the object at an index. + + + List:indexOf (obj) + Returns the index of an object in the List. @@ -103,7 +115,7 @@

Returns:

    - A new list + A new List
@@ -111,8 +123,60 @@
- - List:clear () + + List:__add (obj) +
+
+ Adds an object to the List. + Object must be of reference type + Object may not be the string 'size' + + +

Parameters:

+
    +
  • obj + Object to add +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + List:__remove (obj) +
+
+ Removes an object from the List. + + +

Parameters:

+
    +
  • obj + Object to remove +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + List:__clear ()
Clears the List completely. @@ -130,49 +194,24 @@
- - List:add (obj) + + List:has (obj)
- Adds an object to the List. + Returns true if the List has the object.

Parameters:

  • obj - The object to add + Object to check for

Returns:

    - self -
- - - - -
-
- - List:remove (obj) -
-
- Removes an object from the List. - - -

Parameters:

-
    -
  • obj - The object to remove -
  • -
- -

Returns:

-
    - - self + True if the List has the object, false otherwise
@@ -181,23 +220,23 @@
- List:get (index) + List:get (i)
- Gets an object by numerical index. + Returns the object at an index.

Parameters:

    -
  • index - The index to look at +
  • i + Index to get from

Returns:

    - The object at the index + Object at the index
@@ -205,21 +244,25 @@
- - List:has (obj) + + List:indexOf (obj)
- Gets if the List has the object. + Returns the index of an object in the List.

Parameters:

  • obj - The object to search for - true if the list has the object, false otherwise + Object to get index of
+

Returns:

+
    + + index of object in the List. +
@@ -232,7 +275,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/pool.html b/docs/modules/pool.html index 4111b1e..ed53743 100644 --- a/docs/modules/pool.html +++ b/docs/modules/pool.html @@ -38,13 +38,20 @@

Modules

@@ -52,7 +59,9 @@

Module pool

-

Pool

+

Pool + A Pool is used to iterate over Entities with a specific Components + A Pool contain a any amount of Entities.

@@ -63,9 +72,29 @@ Creates a new Pool - Pool:eligible (e) + Pool:__eligible (e) Checks if an Entity is eligible for the Pool. + + Pool:__add (e) + Internal: Adds an Entity to the Pool. + + + Pool:__remove (e) + Internal: Removed an Entity from the Pool. + + + Pool:getName () + Gets the name of the Pool + + + Pool:getFilter () + Gets the filter of the Pool. + + + Pool:onEntityAdded (e) + Callback for when an Entity is added to the Pool. +
@@ -86,10 +115,10 @@

Parameters:

  • name - Identifier for the Pool. + Name for the Pool.
  • filter - Table containing the required Components + Table containing the required BaseComponents
@@ -104,8 +133,8 @@
- - Pool:eligible (e) + + Pool:__eligible (e)
Checks if an Entity is eligible for the Pool. @@ -114,7 +143,7 @@

Parameters:

  • e - The Entity to check + Entity to check
@@ -127,6 +156,115 @@ +
+
+ + Pool:__add (e) +
+
+ Internal: Adds an Entity to the Pool. + + +

Parameters:

+
    +
  • e + Entity to add +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + Pool:__remove (e) +
+
+ Internal: Removed an Entity from the Pool. + + +

Parameters:

+
    +
  • e + Entity to remove +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + Pool:getName () +
+
+ Gets the name of the Pool + + + +

Returns:

+
    + + Name of the Pool. +
+ + + + +
+
+ + Pool:getFilter () +
+
+ Gets the filter of the Pool. + Warning: Do not modify this filter. + + + +

Returns:

+
    + + Filter of the Pool. +
+ + + + +
+
+ + Pool:onEntityAdded (e) +
+
+ Callback for when an Entity is added to the Pool. + + +

Parameters:

+
    +
  • e + Entity that was added. +
  • +
+ + + + +
@@ -135,7 +273,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/system.html b/docs/modules/system.html index 8f14c13..8011319 100644 --- a/docs/modules/system.html +++ b/docs/modules/system.html @@ -38,13 +38,20 @@

Modules

@@ -52,63 +59,62 @@

Module system

-

System

-

+

System + A System iterates over Entities.

+

From these Entities its get Components and modify them. + A System contains 1 or more Pools. + A System is contained by 1 World.

Functions

- + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + +
System.new (...)Creates a new System prototype.Creates a new SystemClass.
System:__buildPool (baseFilter)Builds a Pool for the System.System.__buildPool (baseFilter)Internal: Builds a Pool for the System.
System:__check (e)Checks and applies an Entity to the System's pools.System:__evaluate (e)Internal: Evaluates an Entity for all the System's Pools.
System:__remove (e)Removed an Entity from the System.Internal: Removes an Entity from the System.
System:__tryAdd (e)Tries to add an Entity to the System.System:clear ()Internal: Clears all Entities from the System.
System:__tryRemove (e)Tries to remove an Entity from the System.System:enable ()Enables the System.
System:getInstance ()Returns the Instance the System is in.System:disable ()Disables the System.
System:__has (e)Returns if the System has the Entity.System:toggleEnable ()Toggles if the System is enabled.
System:init (...)Default callback for system initialization.System:setEnabled (enable)Sets if the System is enabled
System:entityAdded (e)Default callback for adding an Entity.System:isEnabled ()Returns is the System is enabled
System:entityAddedTo (e, pool)Default callback for adding an Entity to a pool.System:getWorld ()Returns the World the System is in.
System:entityRemoved (e)Default callback for removing an Entity.
System:entityRemovedFrom (e, pool)Default callback for removing an Entity from a pool.System:init (world)Callback for system initialization.
@@ -124,7 +130,7 @@ System.new (...)
- Creates a new System prototype. + Creates a new SystemClass.

Parameters:

@@ -137,7 +143,7 @@

Returns:

    - A new System prototype + A new SystemClass
@@ -145,11 +151,11 @@
- - System:__buildPool (baseFilter) + + System.__buildPool (baseFilter)
- Builds a Pool for the System. + Internal: Builds a Pool for the System.

Parameters:

@@ -170,11 +176,11 @@
- - System:__check (e) + + System:__evaluate (e)
- Checks and applies an Entity to the System's pools. + Internal: Evaluates an Entity for all the System's Pools.

Parameters:

@@ -187,7 +193,7 @@

Returns:

    - True if the Entity was added, false if it was removed. Nil if nothing happend + self
@@ -199,7 +205,7 @@ System:__remove (e)
- Removed an Entity from the System. + Internal: Removes an Entity from the System.

Parameters:

@@ -209,64 +215,10 @@ - - - - -
-
- - System:__tryAdd (e) -
-
- Tries to add an Entity to the System. - - -

Parameters:

-
    -
  • e - The Entity to add -
  • -
- - - - - -
-
- - System:__tryRemove (e) -
-
- Tries to remove an Entity from the System. - - -

Parameters:

-
    -
  • e - The Entity to remove -
  • -
- - - - - -
-
- - System:getInstance () -
-
- Returns the Instance the System is in. - - -

Returns:

    - The Instance + self
@@ -274,24 +226,138 @@
- - System:__has (e) + + System:clear ()
- Returns if the System has the Entity. + Internal: Clears all Entities from the System. + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + System:enable () +
+
+ Enables the System. + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + System:disable () +
+
+ Disables the System. + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + System:toggleEnable () +
+
+ Toggles if the System is enabled. + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + System:setEnabled (enable) +
+
+ Sets if the System is enabled

Parameters:

    -
  • e - The Entity to check for +
  • enable + Enable

Returns:

    - True if the System has the Entity. False otherwise + self +
+ + + + +
+
+ + System:isEnabled () +
+
+ Returns is the System is enabled + + + +

Returns:

+
    + + True if the System is enabled, false otherwise +
+ + + + +
+
+ + System:getWorld () +
+
+ Returns the World the System is in. + + + +

Returns:

+
    + + The World the System is in
@@ -300,102 +366,16 @@
- System:init (...) + System:init (world)
- Default callback for system initialization. + Callback for system initialization.

Parameters:

    -
  • ... - Varags -
  • -
- - - - - -
-
- - System:entityAdded (e) -
-
- Default callback for adding an Entity. - - -

Parameters:

-
    -
  • e - The Entity that was added -
  • -
- - - - - -
-
- - System:entityAddedTo (e, pool) -
-
- Default callback for adding an Entity to a pool. - - -

Parameters:

-
    -
  • e - The Entity that was added -
  • -
  • pool - The pool the Entity was added to -
  • -
- - - - - -
-
- - System:entityRemoved (e) -
-
- Default callback for removing an Entity. - - -

Parameters:

-
    -
  • e - The Entity that was removed -
  • -
- - - - - -
-
- - System:entityRemovedFrom (e, pool) -
-
- Default callback for removing an Entity from a pool. - - -

Parameters:

-
    -
  • e - The Entity that was removed -
  • -
  • pool - The pool the Entity was removed from +
  • world + The World the System was added to
@@ -411,7 +391,7 @@
generated by LDoc 1.4.6 -Last updated 2018-09-25 18:42:43 +Last updated 2020-01-04 00:43:06
diff --git a/docs/modules/systems.html b/docs/modules/systems.html new file mode 100644 index 0000000..d6bf949 --- /dev/null +++ b/docs/modules/systems.html @@ -0,0 +1,116 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module systems

+

Systems + Container for registered SystemClasses

+

+ + +

Functions

+ + + + + +
Systems.register (name, systemClass)Registers a SystemClass.
+ +
+
+ + +

Functions

+ +
+
+ + Systems.register (name, systemClass) +
+
+ Registers a SystemClass. + + +

Parameters:

+
    +
  • name + Name to register under +
  • +
  • systemClass + SystemClass to register +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/type.html b/docs/modules/type.html new file mode 100644 index 0000000..377c972 --- /dev/null +++ b/docs/modules/type.html @@ -0,0 +1,292 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module type

+

Type + Helper module to do easy type checking for Concord types

+

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type.isEntity (t)Returns if object is an Entity.
Type.isComponentClass (t)Returns if object is a ComponentClass.
Type.isComponent (t)Returns if object is a Component.
Type.isSystemClass (t)Returns if object is a SystemClass.
Type.isSystem (t)Returns if object is a System.
Type.isWorld (t)Returns if object is a World.
Type.isAssemblage (t)Returns if object is an Assemblage.
+ +
+
+ + +

Functions

+ +
+
+ + Type.isEntity (t) +
+
+ Returns if object is an Entity. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an Entity, false otherwise +
+ + + + +
+
+ + Type.isComponentClass (t) +
+
+ Returns if object is a ComponentClass. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an ComponentClass, false otherwise +
+ + + + +
+
+ + Type.isComponent (t) +
+
+ Returns if object is a Component. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an Component, false otherwise +
+ + + + +
+
+ + Type.isSystemClass (t) +
+
+ Returns if object is a SystemClass. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an SystemClass, false otherwise +
+ + + + +
+
+ + Type.isSystem (t) +
+
+ Returns if object is a System. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an System, false otherwise +
+ + + + +
+
+ + Type.isWorld (t) +
+
+ Returns if object is a World. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an World, false otherwise +
+ + + + +
+
+ + Type.isAssemblage (t) +
+
+ Returns if object is an Assemblage. + + +

Parameters:

+
    +
  • t + Object to check +
  • +
+ +

Returns:

+
    + + True if object is an Assemblage, false otherwise +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/utils.html b/docs/modules/utils.html new file mode 100644 index 0000000..925eb00 --- /dev/null +++ b/docs/modules/utils.html @@ -0,0 +1,116 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module utils

+

Utils + Helper module for misc operations

+

+ + +

Functions

+ + + + + +
Utils.shallowCopy (orig, target)Does a shallow copy of a table and appends it to a target table.
+ +
+
+ + +

Functions

+ +
+
+ + Utils.shallowCopy (orig, target) +
+
+ Does a shallow copy of a table and appends it to a target table. + + +

Parameters:

+
    +
  • orig + Table to copy +
  • +
  • target + Table to append to +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/world.html b/docs/modules/world.html new file mode 100644 index 0000000..8338c23 --- /dev/null +++ b/docs/modules/world.html @@ -0,0 +1,453 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module world

+

World + A World is a collection of Systems and Entities + A world emits to let Systems iterate + A World contains any amount of Systems + A World contains any amount of Entities

+

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
World.new ()Creates a new World.
World:addEntity (e)Adds an Entity to the World.
World:removeEntity (e)Removes an Entity from the World.
World:__dirtyEntity (e)Internal: Marks an Entity as dirty.
World:__flush ()Internal: Flushes all changes to Entities.
World:addSystem (systemClass)Adds a System to the World.
World:addSystems (...)Adds multiple Systems to the World.
World:hasSystem (systemClass)Returns if the World has a System.
World:getSystem (systemClass)Gets a System from the World.
World:emit (functionName, ...)Emits a callback in the World.
World:clear ()Removes all entities from the World
World:onEntityAdded (e)Callback for when an Entity is added to the World.
World:onEntityRemoved (e)Callback for when an Entity is removed from the World.
+ +
+
+ + +

Functions

+ +
+
+ + World.new () +
+
+ Creates a new World. + + + +

Returns:

+
    + + The new World +
+ + + + +
+
+ + World:addEntity (e) +
+
+ Adds an Entity to the World. + + +

Parameters:

+
    +
  • e + Entity to add +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + World:removeEntity (e) +
+
+ Removes an Entity from the World. + + +

Parameters:

+
    +
  • e + Entity to remove +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + World:__dirtyEntity (e) +
+
+ Internal: Marks an Entity as dirty. + + +

Parameters:

+
    +
  • e + Entity to mark as dirty +
  • +
+ + + + + +
+
+ + World:__flush () +
+
+ Internal: Flushes all changes to Entities. + This processes all entities. Adding and removing entities, as well as reevaluating dirty entities. + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + World:addSystem (systemClass) +
+
+ Adds a System to the World. + Callbacks are registered automatically + Entities added before are added to the System retroactively + + +

Parameters:

+
    +
  • systemClass + SystemClass of System to add +
  • +
+ +

Returns:

+
    + + self +
+ + +

See also:

+ + + +
+
+ + World:addSystems (...) +
+
+ Adds multiple Systems to the World. + Callbacks are registered automatically + + +

Parameters:

+
    +
  • ... + SystemClasses of Systems to add +
  • +
+ +

Returns:

+
    + + self +
+ + +

See also:

+ + + +
+
+ + World:hasSystem (systemClass) +
+
+ Returns if the World has a System. + + +

Parameters:

+
    +
  • systemClass + SystemClass of System to check for +
  • +
+ +

Returns:

+
    + + True if World has System, false otherwise +
+ + + + +
+
+ + World:getSystem (systemClass) +
+
+ Gets a System from the World. + + +

Parameters:

+
    +
  • systemClass + SystemClass of System to get +
  • +
+ +

Returns:

+
    + + System to get +
+ + + + +
+
+ + World:emit (functionName, ...) +
+
+ Emits a callback in the World. + Calls all functions with the functionName of added Systems + + +

Parameters:

+
    +
  • functionName + Name of functions to call. +
  • +
  • ... + Parameters passed to System's functions +
  • +
+ +

Returns:

+
    + + self +
+ + + + +
+
+ + World:clear () +
+
+ Removes all entities from the World + + + +

Returns:

+
    + + self +
+ + + + +
+
+ + World:onEntityAdded (e) +
+
+ Callback for when an Entity is added to the World. + + +

Parameters:

+
    +
  • e + The Entity that was added +
  • +
+ + + + + +
+
+ + World:onEntityRemoved (e) +
+
+ Callback for when an Entity is removed from the World. + + +

Parameters:

+
    +
  • e + The Entity that was removed +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + diff --git a/docs/modules/worlds.html b/docs/modules/worlds.html new file mode 100644 index 0000000..74bf7c3 --- /dev/null +++ b/docs/modules/worlds.html @@ -0,0 +1,116 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module worlds

+

Worlds + Container for registered Worlds

+

+ + +

Functions

+ + + + + +
Worlds.register (name, world)Registers a World.
+ +
+
+ + +

Functions

+ +
+
+ + Worlds.register (name, world) +
+
+ Registers a World. + + +

Parameters:

+
    +
  • name + Name to register under +
  • +
  • world + World to register +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 00:43:06 +
+
+ + From 424f27ed8a24f42c462ac57d470b02f9d184b458 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 04:44:21 +0100 Subject: [PATCH 041/113] First version of documentation --- README.md | 288 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 265 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 32f0383..436e7a0 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,281 @@ # Concord -Concord is a feature complete ECS. -It's main focus is on speed and usage. You should be able to quickly write code that performs well. +Concord is a feature complete ECS for LÖVE. +It's main focus is performance and ease of use. +With Concord it is possibile to easily write fast and clean code. -Documentation for Concord can be found in the [Wiki tab](https://github.com/Tjakka5/Concord/wiki). +This readme will explain how to use Concord. -Auto generated docs for Concord can be found in the [Github page](https://tjakka5.github.io/Concord/). These are still work in progress and might be incomplete though. +Additionally all of Concord is documented using the LDoc format. +Auto generated docs for Concord can be found in `docs` folder, or on the [Github page](https://tjakka5.github.io/Concord/). ## Installation -Download the repository and drop it in your project, then simply require it as: +Download the repository and copy the 'src' folder in your project. Rename it to something that makes sense (Probably 'concord'), then require it in your project like so: ```lua -local Concord = require(PathToConcord).init() - -You will only need to call .init once when you first require it. +local Concord = require("path.to.concord") ``` -## Modules -Below is a list of modules. -More information about what each done can be found in the Wiki +Concord has a bunch of modules. These can be accessed through Concord: ```lua -local Concord = require("concord") -local Entity = require("concord.entity") -local Component = require("concord.component") -local System = require("concord.system") -local Instance = require("concord.instance") +-- Modules +local Entity = Concord.entity +local Component = Concord.component +local System = Concord.system +local World = Concord.world +local Assemblage = Concord.assemblage + +-- Containers +local Components = Concord.components +local Systems = Concord.systems +local Worlds = Concord.worlds +local Assemblages = Concord.assemblages ``` +## ECS +Concord is an Entity Component System (ECS for short) library. +This is a coding paradigm where _composition_ is used over _inheritance_. +Because of this it is easier to write more modular code. It often allowes you to combine any form of behaviour for the objects in your game (Entities). + +As the name might suggest, ECS consists of 3 core things: Entities, Components, and Systems. A proper understanding of these is required to use Concord effectively. +We'll start with the simplest one. + +### Components +Components are pure raw data. In Concord this is just a table with some fields. +A position component might look like +`{ x = 100, y = 50}`, whereas a health Component might look like `{ currentHealth = 10, maxHealth = 100 }`. +What is most important is that Components are data and nothing more. They have 0 functionality. + +### Entities +Entities are the actual objects in your game. Like a player, an enemy, a crate, or a bullet. +Every Entity has it's own set of Components, with their own values. + +A crate might have the following components: +```lua +{ + position = { x = 100, y = 200 }, + texture = { path = "crate.png", image = Image }, + pushable = { }, +} +``` + +Whereas a player might have the following components: +```lua +{ + position = { x = 200, y = 300 }, + texture = { path = "player.png", image = Image }, + controllable = { keys = "wasd" }, + health = { currentHealth = 10, maxHealth = 100}, +} +``` + +Any Component can be given to any Entity (once). Which Components an Entity has will determine how it behaves. This is done through the last thing... + +### Systems +Systems are the things that actually _do_ stuff. They contain all your fancy algorithms and cool game logic. +Each System will do one specific task like say, drawing Entities. +For this they will only act on Entities that have the Components needed for this: `position` and `texture`. All other Components are irrelevant. + +In Concord this is done something alike this: + +```lua +drawSystem = System({position, texture}) -- Define a System that takes all Entities with a position and texture Component + +function drawSystem:draw() -- Give it a draw function + for _, entity in ipairs(self.pool) do -- Iterate over all Entities that this System acts on + local position = entity[position] -- Get the position Component of this Entity + local texture = entity[texture] -- Get the texture Component of this Entity + + -- Draw the Entity + love.graphics.draw(texture.image, position.x, position.y) + end +end +``` + +### To summarize... +- Components contain only data. +- Entities contain any set of Components. +- Systems act on Entities that have a required set of Components. + +By creating Components and Systems you create modular behaviour that can apply to any Entity. +What if we took our crate from before and gave it the `controllable` Component? It would respond to our user input of course. + +Or what if the enemy shot bullets with a `health` Component? It would create bullets that we'd be able to destroy by shooting them. + +And all that without writing a single extra line of code. Just reusing code that already existed and is guaranteed to be reuseable. + +## Documentation + +### General design + +#### Classes +Concord does a few things that might not be immediately clear. This segment should help understanding. + +When you define a Component or System you are actually defining a `ComponentClass` and `SystemClass` respectively. From these instances of them can be created. They also act as identifiers for Concord. + +For example. If you want to get a specific Component from an Entity, you'd do `Component = Entity:get(ComponentClass)`. +When ComponentClasses or SystemClasses are required it will be written clearly in the Documentation. + +#### Containers +Since you'll be defining or creating lots of Components, Systems, Worlds and Assemblages Concord adds container tables for each of them so that they are easily accessible. + +These containers can be accessed through +```lua +local Components = require("path.to.concord").components +local Systems = require("path.to.concord").systems +local Worlds = require("path.to.concord").worlds +local Assemblages = require("path.to.concord").aorlds +``` + +Concord has helper functions to fill these containers. There are the following options depending on your needs / preference: +```lua +-- Loads each file. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' ) +Concord.loadComponents({"path.to.component_1", "path.to.component_2", "etc"}) + +-- Loads all files in the directory. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' ) +Concord.loadComponents("path.to.directory.containing.components") + + +-- Put the ComponentClass into the container directly. Useful if you want more manual control. Note that you need to require the file in this case +Components.register("componentName", ComponentClass) +``` +Things can then be accessed through their names: +```lua +local component_1_class = Components.component_1 +local componentName_class = Components.componentName +``` + +All the above applies the same to all the other containers. + +### Components +When defining a ComponentClass you usually pass in a `populate` function. This will fill the Component with values. + +```lua +-- Define the ComponentClass. +-- The component variable is the actual Component given to an Entity +-- The x and y variables are values we pass in when we create the Component +local PositionComponentClass = Concord.component(function(component, x, y) + component.x = x or 0 + component.y = y or 0 +end) + +-- We can manually register the Component to the container if we want +Concord.components.register("positionComponent", PositionComponentClass) + +-- Otherwise we just return the Component so it can be required +return PositionComponentClass +``` + +You can also pass nothing. This will keep the Component empty. +This can be useful for Components that define a boolean state. For example `pushable` or `crouching`. + +### Entities +Entities can be freely made and be given Components. You pass the ComponentClass and the values you want to pass. It will then create the Component for you. + +Entities can only have a maximum of one of each Component. +Entities can not share Components. + +```lua +-- Create a new Entity +local myEntity = Entity() +-- or +local myEntity = Entity(myWorld) -- To add it to a world immediately ( See World ) +``` + +```lua +-- Give the entity the position component defined above +-- x will become 100. y will become 50 +myEntity:give(Concord.components.positionComponent, 100, 50) +``` + +```lua +-- Retrieve a Component +local positionComponent = myEntity[Concord.components.positionComponent] +-- or +local positionComponent = myEntity:get(Concord.components.positionComponents) + +print(positionComponent.x, positionComponent.y) -- 100, 50 +``` + +```lua +-- Remove a Component +myEntity:remove(Concord.compoennts.positionComponent) +``` + +```lua +-- Check if the Entity has a Component +local hasPositionComponent = myEntity:has(Concord.components.positionComponent) +print(hasPositionComponent) -- false +``` + +```lua +-- Entity:give will override a Component if the Entity already had it +-- Entity:ensure will only put the Component if the Entity does not already have it + +Entity:ensure(Concord.components.positionComponents, 0, 0) -- Will give +-- Position is {x = 0, y = 0} + +Entity:give(Concord.components.positionComponent, 50, 50) -- Will override +-- Position is {x = 50, y = 50} + +Entity:give(Concord.components.positionComponent, 100, 100) -- Will override +-- Position is {x = 100, y = 100} + +Entity:ensure(Concord.components.positionComponents, 0, 0) -- Wont do anything +-- Position is {x = 100, y = 100} +``` + +```lua +-- Retrieve all Components +-- WARNING: Do not modify this table. It is read-only +local allComponents = myEntity:getComponents() + +for ComponentClass, Component in ipairs(allComponents) do + -- Do stuff +end +``` + +```lua +-- Assemble the Entity ( See Assemblages ) +myEntity:assemble(myAssemblage, 100, true, "foo") +``` + +```lua +-- Check if the Entity is in a world +local inWorld = myEntity:inWorld() + +-- Get the World the Entity is in +local world = myEntity:getWorld() +``` + +```lua +-- Destroy the Entity +myEntity:destroy() +``` + +### Systems + +TODO + +### Worlds + +TODO + +### Assemblages + +TODO + +### Type + +TODO + ## Contributors -``` -Positive07: Constant support and a good rubberduck -Brbl: Early testing and issue reporting -Josh: Squashed a few bugs and docs -Erasio: Took inspiration from HooECS. Also introduced me to ECS. -``` - +- __Positive07__: Constant support and a good rubberduck +- __Brbl__: Early testing and issue reporting +- __Josh__: Squashed a few bugs and generated docs +- __Erasio__: I took inspiration from HooECS. He also introduced me to ECS +- __Speak__: Lots of testing for new features of Concord +- __Tesselode__: Brainstorming and helpful support + ## Licence MIT Licensed - Copyright Justin van der Leij (Tjakka5) From 451b88cdea6a63df4ca6ee8bd41d44b521ba3274 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 10:47:45 +0100 Subject: [PATCH 042/113] Finish system optimization --- README.md | 4 ++-- src/system.lua | 9 ++++++++- src/world.lua | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 436e7a0..099e929 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ print(positionComponent.x, positionComponent.y) -- 100, 50 ```lua -- Remove a Component -myEntity:remove(Concord.compoennts.positionComponent) +myEntity:remove(Concord.components.positionComponent) ``` ```lua @@ -209,7 +209,7 @@ print(hasPositionComponent) -- false ``` ```lua --- Entity:give will override a Component if the Entity already had it +-- Entity:give will override a Component if the Entity already has it -- Entity:ensure will only put the Component if the Entity does not already have it Entity:ensure(Concord.components.positionComponents, 0, 0) -- Will give diff --git a/src/system.lua b/src/system.lua index 5c12d39..0215b7b 100644 --- a/src/system.lua +++ b/src/system.lua @@ -29,7 +29,7 @@ System.mt = { -- 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(System, system) + Utils.shallowCopy(systemClass, system) end for _, filter in pairs(systemClass.__filter) do @@ -58,6 +58,13 @@ function System.new(...) }, System.mt) systemClass.__index = systemClass + -- Optimization: We deep copy the World class into our instance of a world. + -- 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(System, systemClass) + end + return systemClass end diff --git a/src/world.lua b/src/world.lua index fe6107e..a98d38d 100644 --- a/src/world.lua +++ b/src/world.lua @@ -243,7 +243,7 @@ function World:emit(functionName, ...) error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") end - local listeners = self.events[functionName] + local listeners = self.events[functionName] if listeners then for i = 1, #listeners do From f502f1b9f6887275c8e62bad7b8d66c0756f10da Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 10:50:13 +0100 Subject: [PATCH 043/113] rename folder src to concord --- {src => concord}/assemblage.lua | 0 {src => concord}/assemblages.lua | 0 {src => concord}/component.lua | 0 {src => concord}/components.lua | 0 {src => concord}/entity.lua | 0 {src => concord}/init.lua | 0 {src => concord}/list.lua | 0 {src => concord}/pool.lua | 0 {src => concord}/system.lua | 0 {src => concord}/systems.lua | 0 {src => concord}/type.lua | 0 {src => concord}/utils.lua | 0 {src => concord}/world.lua | 0 {src => concord}/worlds.lua | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename {src => concord}/assemblage.lua (100%) rename {src => concord}/assemblages.lua (100%) rename {src => concord}/component.lua (100%) rename {src => concord}/components.lua (100%) rename {src => concord}/entity.lua (100%) rename {src => concord}/init.lua (100%) rename {src => concord}/list.lua (100%) rename {src => concord}/pool.lua (100%) rename {src => concord}/system.lua (100%) rename {src => concord}/systems.lua (100%) rename {src => concord}/type.lua (100%) rename {src => concord}/utils.lua (100%) rename {src => concord}/world.lua (100%) rename {src => concord}/worlds.lua (100%) diff --git a/src/assemblage.lua b/concord/assemblage.lua similarity index 100% rename from src/assemblage.lua rename to concord/assemblage.lua diff --git a/src/assemblages.lua b/concord/assemblages.lua similarity index 100% rename from src/assemblages.lua rename to concord/assemblages.lua diff --git a/src/component.lua b/concord/component.lua similarity index 100% rename from src/component.lua rename to concord/component.lua diff --git a/src/components.lua b/concord/components.lua similarity index 100% rename from src/components.lua rename to concord/components.lua diff --git a/src/entity.lua b/concord/entity.lua similarity index 100% rename from src/entity.lua rename to concord/entity.lua diff --git a/src/init.lua b/concord/init.lua similarity index 100% rename from src/init.lua rename to concord/init.lua diff --git a/src/list.lua b/concord/list.lua similarity index 100% rename from src/list.lua rename to concord/list.lua diff --git a/src/pool.lua b/concord/pool.lua similarity index 100% rename from src/pool.lua rename to concord/pool.lua diff --git a/src/system.lua b/concord/system.lua similarity index 100% rename from src/system.lua rename to concord/system.lua diff --git a/src/systems.lua b/concord/systems.lua similarity index 100% rename from src/systems.lua rename to concord/systems.lua diff --git a/src/type.lua b/concord/type.lua similarity index 100% rename from src/type.lua rename to concord/type.lua diff --git a/src/utils.lua b/concord/utils.lua similarity index 100% rename from src/utils.lua rename to concord/utils.lua diff --git a/src/world.lua b/concord/world.lua similarity index 100% rename from src/world.lua rename to concord/world.lua diff --git a/src/worlds.lua b/concord/worlds.lua similarity index 100% rename from src/worlds.lua rename to concord/worlds.lua From 90b949f8a539c1a88fe33966f76232ccaa2e0807 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 10:50:41 +0100 Subject: [PATCH 044/113] rename src folder in config.ld --- config.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.ld b/config.ld index a04aeaf..fed6e35 100644 --- a/config.ld +++ b/config.ld @@ -1,3 +1,3 @@ project = 'Concord' description = 'A feature-complete ECS library' -file = {'src', exclude = {}} \ No newline at end of file +file = {'concord', exclude = {}} \ No newline at end of file From cf51bccb8148195b10942871aabc3a1c1cf2b613 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 11:10:29 +0100 Subject: [PATCH 045/113] Fix naming of system:clear, system:setEnable --- README.md | 131 ++++++++++++++++++++++++++++++++++++++------- concord/system.lua | 4 +- concord/world.lua | 2 +- 3 files changed, 114 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 099e929..69da9d4 100644 --- a/README.md +++ b/README.md @@ -151,23 +151,27 @@ All the above applies the same to all the other containers. When defining a ComponentClass you usually pass in a `populate` function. This will fill the Component with values. ```lua --- Define the ComponentClass. +-- Create the ComponentClass with a populate function -- The component variable is the actual Component given to an Entity -- The x and y variables are values we pass in when we create the Component -local PositionComponentClass = Concord.component(function(component, x, y) +local positionComponentClass = Concord.component(function(component, x, y) component.x = x or 0 component.y = y or 0 end) --- We can manually register the Component to the container if we want -Concord.components.register("positionComponent", PositionComponentClass) - --- Otherwise we just return the Component so it can be required -return PositionComponentClass +-- Create a ComponentClass without a populate function +-- Components of this type won't have any fields. +-- This can be useful to indiciate state. +local pushableComponentClass = Concord.component() ``` -You can also pass nothing. This will keep the Component empty. -This can be useful for Components that define a boolean state. For example `pushable` or `crouching`. +```lua +-- Manually register the Component to the container if we want +Concord.components.register("positionComponent", positionComponentClass) + +-- Otherwise return the Component so it can be required +return positionComponentClass +``` ### Entities Entities can be freely made and be given Components. You pass the ComponentClass and the values you want to pass. It will then create the Component for you. @@ -183,28 +187,28 @@ local myEntity = Entity(myWorld) -- To add it to a world immediately ( See World ``` ```lua --- Give the entity the position component defined above +-- Give the entity the position Component defined above -- x will become 100. y will become 50 -myEntity:give(Concord.components.positionComponent, 100, 50) +myEntity:give(positionComponentClass, 100, 50) ``` ```lua -- Retrieve a Component -local positionComponent = myEntity[Concord.components.positionComponent] +local positionComponent = myEntity[positionComponentClass] -- or -local positionComponent = myEntity:get(Concord.components.positionComponents) +local positionComponent = myEntity:get(positionComponentClass) print(positionComponent.x, positionComponent.y) -- 100, 50 ``` ```lua -- Remove a Component -myEntity:remove(Concord.components.positionComponent) +myEntity:remove(positionComponentClass) ``` ```lua -- Check if the Entity has a Component -local hasPositionComponent = myEntity:has(Concord.components.positionComponent) +local hasPositionComponent = myEntity:has(positionComponentClass) print(hasPositionComponent) -- false ``` @@ -212,16 +216,16 @@ print(hasPositionComponent) -- false -- Entity:give will override a Component if the Entity already has it -- Entity:ensure will only put the Component if the Entity does not already have it -Entity:ensure(Concord.components.positionComponents, 0, 0) -- Will give +Entity:ensure(positionComponentClass, 0, 0) -- Will give -- Position is {x = 0, y = 0} -Entity:give(Concord.components.positionComponent, 50, 50) -- Will override +Entity:give(positionComponentClass, 50, 50) -- Will override -- Position is {x = 50, y = 50} -Entity:give(Concord.components.positionComponent, 100, 100) -- Will override +Entity:give(positionComponentClass, 100, 100) -- Will override -- Position is {x = 100, y = 100} -Entity:ensure(Concord.components.positionComponents, 0, 0) -- Wont do anything +Entity:ensure(positionComponentClass, 0, 0) -- Wont do anything -- Position is {x = 100, y = 100} ``` @@ -255,7 +259,94 @@ myEntity:destroy() ### Systems -TODO +Systems are definded as a SystemClass. Concord will automatically create an instance of a System when it is needed. + +Systems get access to Entities through `pools`. They are created using a filter. +Systems can have multiple pools. + +```lua +-- Create a System +local mySystemClass = Concord.system({positionComponentClass}) -- Pool will contain all Entities with a position Component + +-- Create a System with multiple pools +local mySystemClass = Concord.system( + { -- This Pool's name will default to 'pool' + positionCompomponentClass, + velocityComponentClass, + }, + { -- This Pool's name will be 'secondPool' + healthComponentClass, + damageableComponentClass, + "secondPool", + } +) +``` + +```lua +-- Manually register the Component to the container if we want +Concord.system.register("mySystem", mySystemClass) + +-- Otherwise return the Component so it can be required +return mySystemClass +``` + +```lua +-- If a System has a :init function it will be called on creation + +-- world is the World the System was created for +function mySystemClass:init(world) + -- Do stuff +end +``` + +```lua +-- Defining a function +function mySystemClass:update(dt) + -- Iterate over all entities in the Pool + for _, e in ipairs(self.pool) + -- Get the Entity's Components + local positionComponent = e[positionComponentClass] + local velocityComponent = e[velocityComponentClass] + + -- Do something with the Components + positionComponent.x = positionComponent.x + velocityComponent.x * dt + positionComponent.y = positionComponent.y + velocityComponent.y * dt + end + + + -- Iterate over all entities in the second Pool + for _, e in ipairs(self.secondPool) + -- Do something + end +end +``` + +```lua +-- Systems can be enabled and disabled +-- Systems are enabled by default + +-- Enable a System +mySystem:enable() +-- or +mySystem:setEnable(true) + +-- Disable a System +mySystem:disable() +-- or +mySystem:setEnable(false) + +-- Toggle the enable state +mySystem:toggleEnabled() + +-- Get enabled state +local isEnabled = mySystem:isEnabled() +print(isEnabled) -- true +``` + +```lua +-- Get the World the System is in +local world = System:getWorld() +``` ### Worlds diff --git a/concord/system.lua b/concord/system.lua index 0215b7b..589d8ac 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -119,7 +119,7 @@ end --- Internal: Clears all Entities from the System. -- @return self -function System:clear() +function System:__clear() for i = 1, #self.__pools do self.__pools[i]:__clear() end @@ -145,7 +145,7 @@ end --- Toggles if the System is enabled. -- @return self -function System:toggleEnable() +function System:toggleEnabled() self:setEnabled(not self.__enabled) return self diff --git a/concord/world.lua b/concord/world.lua index a98d38d..4fbcc9a 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -268,7 +268,7 @@ function World:clear() end for i = 1, self.systems.size do - self.systems[i]:clear() + self.systems[i]:__clear() end return self From 3b52f28fe03fd025cd24e3b55bc4e442e11b61f0 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 11:46:10 +0100 Subject: [PATCH 046/113] Finish first version of documentation --- README.md | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 69da9d4..bf8d423 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,25 @@ This readme will explain how to use Concord. Additionally all of Concord is documented using the LDoc format. Auto generated docs for Concord can be found in `docs` folder, or on the [Github page](https://tjakka5.github.io/Concord/). +--- + +## Table of Contents +[Installation](#installation) +[ECS](#ecs) +[API](#api) : +- [Components](#components) +- [Entities](#entities) +- [Systems](#systems) +- [Worlds](#worlds) +- [Assemblages](#assemblages) + +[Quick Example](#quick-example) +[Contributors](#contributors) +[License](#licence) + + +--- + ## Installation Download the repository and copy the 'src' folder in your project. Rename it to something that makes sense (Probably 'concord'), then require it in your project like so: ```lua @@ -32,6 +51,8 @@ local Worlds = Concord.worlds local Assemblages = Concord.assemblages ``` +--- + ## ECS Concord is an Entity Component System (ECS for short) library. This is a coding paradigm where _composition_ is used over _inheritance_. @@ -104,13 +125,16 @@ Or what if the enemy shot bullets with a `health` Component? It would create bul And all that without writing a single extra line of code. Just reusing code that already existed and is guaranteed to be reuseable. -## Documentation +--- + +## API ### General design -#### Classes Concord does a few things that might not be immediately clear. This segment should help understanding. +#### Classes + When you define a Component or System you are actually defining a `ComponentClass` and `SystemClass` respectively. From these instances of them can be created. They also act as identifiers for Concord. For example. If you want to get a specific Component from an Entity, you'd do `Component = Entity:get(ComponentClass)`. @@ -147,6 +171,26 @@ local componentName_class = Components.componentName All the above applies the same to all the other containers. +#### Method chaining +```lua +-- All functions that do something ( eg. Don't return anything ) will return self +-- This allowes you to chain methods + +entity +:give(position, 100, 50) +:give(velocity, 200, 0) +:remove(position) +:destroy() + +-- + +world +:addEntity(fooEntity) +:addEntity(barEntity) +:clear() +:emit("test") +``` + ### Components When defining a ComponentClass you usually pass in a `populate` function. This will fill the Component with values. @@ -166,10 +210,10 @@ local pushableComponentClass = Concord.component() ``` ```lua --- Manually register the Component to the container if we want +-- Manually register the ComponentClass to the container if we want Concord.components.register("positionComponent", positionComponentClass) --- Otherwise return the Component so it can be required +-- Otherwise return the ComponentClass so it can be required return positionComponentClass ``` @@ -283,10 +327,10 @@ local mySystemClass = Concord.system( ``` ```lua --- Manually register the Component to the container if we want +-- Manually register the SystemClass to the container if we want Concord.system.register("mySystem", mySystemClass) --- Otherwise return the Component so it can be required +-- Otherwise return the SystemClass so it can be required return mySystemClass ``` @@ -350,15 +394,192 @@ local world = System:getWorld() ### Worlds -TODO +Worlds are the thing your System and Entities live in. +With Worlds you can `:emit` a callback. All Systems with this callback will then be called. + +Worlds can have 1 instance of every SystemClass. +Worlds can have any number of Entities. + +```lua +-- Create World +local myWorld = Concord.world() +``` + +```lua +-- Manually register the World to the container if we want +Concord.worlds.register("myWorld", myWorld) + +-- Otherwise return the World so it can be required +return myWorld +``` + +```lua +-- Add an Entity to the World +myWorld:addEntity(myEntity) + +-- Remove an Entity from the World +myWorld:removeEntity(myEntity) +``` + +```lua +-- Add a System to the World +myWorld:addSystem(mySystemClass) + +-- Add multiple Systems to the World +myWorld:addSystems(moveSystemClass, renderSystemClass, controlSystemClass) +``` + +```lua +-- Check if the World has a System +local hasSystem = myWorld:hasSystem(mySystemClass) + +-- Get a System from the World +local mySystem = myWorld:getSystem(mySystemClass) +``` + +```lua +-- Emit an event + +-- This will call the 'update' function of any added Systems +-- They will be called in the order they were added +myWorld:emit("update", dt) + +-- You can emit any event with any parameters +myWorld:emit("customCallback", 100, true, "Hello World") +``` + +```lua +-- Remove all Entities from the World +myWorld:clear() +``` + +```lua +-- Override-able callbacks + +-- Called when an Entity is added to the World +-- e is the Entity added +function myWorld:onEntityAdded(e) + -- Do something +end + +-- Called when an Entity is removed from the World +-- e is the Entity removed +function myWorld:onEntityRemoved(e) + -- Do something +end +``` ### Assemblages -TODO +Assemblages are helpers to 'make' Entities something. +An important distinction is that they _append_ Components. -### Type +```lua +-- Make an Assemblage +-- e is the Entity being assembled. +-- cuteness and legs are variables passed in +local animalAssemblage(function(e, cuteness, legs) + e + :give(cutenessComponentClass, cuteness) + :give(limbs, legs, 0) -- Variable amount of legs. 0 arm. +end) -TODO +-- Make an Assemblage that used animalAssemblage +-- cuteness is a variables passed in +local catAssemblage(function(e, cuteness) + e + :assemble(animalAssemblage, cuteness * 2, 4) -- Cats are twice as cute, and have 4 legs. + :give(soundComponent, "meow.mp3") +end) +``` + +```lua +-- Use an Assemblage +myEntity:assemble(catAssemblage, 100) -- 100 cuteness +-- or +catAssemblage:assemble(myEntity, 100) -- 100 cuteness +``` + +--- + +## Quick Example +```lua +local Concord = require("concord") + +-- Defining ComponentClasses +-- I use UpperCamelCase to indicate its a class +local Position = Concord.component(function(c, x, y) + c.x = x or 0 + c.y = y or 0 +end) + +local Velocity = Concord.component(function(c, x, y) + c.x = x or 0 + c.y = y or 0 +end) + +local Drawable = Concord.component() + + +-- Defining Systems +local MoveSystem = Concord.system({Position, Velocity}) + +function MoveSystem:update(dt) + for _, e in ipairs(self.pool) do + -- I use lowerCamelCase to indicate its an instance + local position = e[Position] + local velocity = e[Velocity] + + position.x = position.x + velocity.x * dt + position.y = position.y + velocity.y * dt + end +end + + +local DrawSystem = Concord.System({Position, Drawable}) + +function DrawSystem:draw() + for _, e in ipairs(self.pool) do + local position = e[Position] + + love.graphics.circle("fill", position.x, position.y, 5) + end +end + + +-- Create the World +local world = World() + +-- Add the Systems +world:addSystems(MoveSystem, DrawSystem) + +-- This Entity will be rendered on the screen, and move to the right at 100 pixels a second +local entity_1 = Concord.entity(world) +:give(Position, 100, 100) +:give(Velocity, 100, 0) +:give(Drawable) + +-- This Entity will be rendered on the screen, and stay at 50, 50 +local entity_2 = Concord.entity(world) +:give(Position, 50, 50) +:give(Drawable) + +-- This Entity does exist in the World, but since it doesn't match any System's filters it won't do anything +local entity_2 = Concord.entity(world) +:give(Position, 200, 200) + + +-- Emit the events +function love.update(dt) + world:emit(dt) +end + +function love.draw() + world:draw() +end +``` + +--- ## Contributors - __Positive07__: Constant support and a good rubberduck @@ -367,6 +588,8 @@ TODO - __Erasio__: I took inspiration from HooECS. He also introduced me to ECS - __Speak__: Lots of testing for new features of Concord - __Tesselode__: Brainstorming and helpful support - + +--- + ## Licence MIT Licensed - Copyright Justin van der Leij (Tjakka5) From c217183cb989a3d9052cbb936514b4442ba60105 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 12:24:15 +0100 Subject: [PATCH 047/113] Fix typos in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf8d423..1b8b7fc 100644 --- a/README.md +++ b/README.md @@ -440,7 +440,7 @@ local mySystem = myWorld:getSystem(mySystemClass) ```lua -- Emit an event --- This will call the 'update' function of any added Systems +-- This will call the 'update' function of all added Systems if they have one -- They will be called in the order they were added myWorld:emit("update", dt) @@ -565,7 +565,7 @@ local entity_2 = Concord.entity(world) :give(Drawable) -- This Entity does exist in the World, but since it doesn't match any System's filters it won't do anything -local entity_2 = Concord.entity(world) +local entity_3 = Concord.entity(world) :give(Position, 200, 200) From 6cd66e6737fe19d90b6565f170a0db2b20282744 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 13:26:26 +0100 Subject: [PATCH 048/113] Add serialization and deserialization functions to component, entity, world --- concord/component.lua | 35 ++++++++++--- concord/components.lua | 12 ++++- concord/entity.lua | 38 +++++++++++++- concord/world.lua | 92 ++++++++++++++++++++++++--------- examples/serialization/init.lua | 69 +++++++++++++++++++++++++ main.lua | 1 + 6 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 examples/serialization/init.lua create mode 100644 main.lua diff --git a/concord/component.lua b/concord/component.lua index bf862a0..eece914 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -18,6 +18,7 @@ function Component.new(populate) local componentClass = setmetatable({ __populate = populate, + __name = nil, __isComponentClass = true, }, Component.__mt) @@ -32,22 +33,44 @@ end function Component:__populate() -- luacheck: ignore end +function Component:serialize() -- luacheck: ignore +end + +function Component:deserialize(data) -- luacheck: ignore +end + +--- Internal: Creates a new Component. +-- @return A new Component +function Component:__new() + local component = setmetatable({ + __componentClass = self, + + __isComponent = true, + __isComponentClass = false, + }, self.__mt) + + return component +end + --- Internal: Creates and populates a new Component. -- @param ... Varargs passed to the populate function -- @return A new populated Component function Component:__initialize(...) - local component = setmetatable({ - __componentClass = self, - - __isComponent = true, - __isComponentClass = false, - }, self) + local component = self:__new() self.__populate(component, ...) return component end +function Component:hasName() + return self.__name and true or false +end + +function Component:getName() + return self.__name +end + return setmetatable(Component, { __call = function(_, ...) return Component.new(...) diff --git a/concord/components.lua b/concord/components.lua index ba88c7e..522cf78 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -20,12 +20,22 @@ function Components.register(name, componentClass) end if (rawget(Components, name)) then - error("bad argument #2 to 'Components.register' (ComponentClass with name '"..name.."' was already registerd)", 3) + error("bad argument #2 to 'Components.register' (ComponentClass with name '"..name.."' was already registerd)", 3) -- luacheck: ignore end Components[name] = componentClass + componentClass.__name = name end +function Components.has(name) + return Components[name] and true or false +end + +function Components.get(name) + return Components[name] +end + + return setmetatable(Components, { __index = function(_, name) error("Attempt to index ComponentClass '"..tostring(name).."' that does not exist / was not registered", 2) diff --git a/concord/entity.lua b/concord/entity.lua index cce5213..8359fd8 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -5,7 +5,8 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Type = require(PATH..".type") +local Components = require(PATH..".components") +local Type = require(PATH..".type") local Entity = {} Entity.__mt = { @@ -173,6 +174,41 @@ function Entity:getWorld() return self.__world end +function Entity:serialize() + local data = {} + + for _, component in pairs(self.__components) do + if component.__name then + local componentData = component:serialize() + componentData.__name = component.__name + + data[#data + 1] = componentData + end + end + + return data +end + +function Entity:deserialize(data) + for i = 1, #data do + local componentData = data[i] + + if (not Components[componentData.__name]) then + error("bad argument #1 to 'Entity:deserialize' (ComponentClass "..type(componentData.__name).." wasn't yet loaded)") -- luacheck: ignore + end + + local componentClass = Components[componentData.__name] + + local component = componentClass:__new() + component:deserialize(componentData) + + self[componentClass] = component + self.__components[componentClass] = component + + self:__dirty() + end +end + return setmetatable(Entity, { __call = function(_, ...) return Entity.new(...) diff --git a/concord/world.lua b/concord/world.lua index 4fbcc9a..18520cd 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -6,9 +6,10 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Type = require(PATH..".type") -local List = require(PATH..".list") -local Utils = require(PATH..".utils") +local Entity = require(PATH..".entity") +local Type = require(PATH..".type") +local List = require(PATH..".list") +local Utils = require(PATH..".utils") local World = { ENABLE_OPTIMIZATION = true, @@ -21,10 +22,10 @@ World.__mt = { -- @return The new World function World.new() local world = setmetatable({ - entities = List(), - systems = List(), + __entities = List(), + __systems = List(), - events = {}, + __events = {}, __added = List(), __backAdded = List(), __removed = List(), __backRemoved = List(), @@ -104,10 +105,10 @@ function World:__flush() for i = 1, self.__backAdded.size do e = self.__backAdded[i] - self.entities:__add(e) + 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) @@ -119,10 +120,10 @@ function World:__flush() e = self.__backRemoved[i] e.__world = nil - self.entities:__remove(e) + 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) @@ -133,8 +134,8 @@ function World:__flush() for i = 1, self.__backDirty.size do e = self.__backDirty[i] - 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 end self.__backDirty:__clear() @@ -168,18 +169,18 @@ 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 if (not blacklistedSystemFunctions[callbackName]) then -- Make container for all listeners of the callback if it does not exist yet - if (not self.events[callbackName]) then - self.events[callbackName] = {} + if (not self.__events[callbackName]) then + self.__events[callbackName] = {} end -- Add callback to listeners - local listeners = self.events[callbackName] + local listeners = self.__events[callbackName] listeners[#listeners + 1] = { system = system, callback = callback, @@ -188,8 +189,8 @@ function World:addSystem(systemClass) end -- Evaluate all existing entities - for j = 1, self.entities.size do - system:__evaluate(self.entities[j]) + for j = 1, self.__entities.size do + system:__evaluate(self.__entities[j]) end return self @@ -243,7 +244,7 @@ function World:emit(functionName, ...) error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") end - local listeners = self.events[functionName] + local listeners = self.__events[functionName] if listeners then for i = 1, #listeners do @@ -263,17 +264,58 @@ end --- Removes all entities from the World -- @return self function World:clear() - for i = 1, self.entities.size do - self:removeEntity(self.entities[i]) + for i = 1, self.__entities.size do + self:removeEntity(self.__entities[i]) end - for i = 1, self.systems.size do - self.systems[i]:__clear() + for i = 1, self.__systems.size do + self.__systems[i]:__clear() end return self end +function World:getEntities() + return self.__entities +end + +function World:getSystems() + return self.__systems +end + +function World:serialize() + self:__flush() + + local data = {} + + for i = 1, self.__entities.size do + local entity = self.__entities[i] + + local entityData = entity:serialize() + + data[i] = entityData + end + + return data +end + +function World:deserialize(data, append) + if (not append) then + self:clear() + end + + for i = 1, #data do + local entityData = data[i] + + local entity = Entity() + entity:deserialize(entityData) + + self:addEntity(entity) + end + + self:__flush() +end + --- Callback for when an Entity is added to the World. -- @param e The Entity that was added function World:onEntityAdded(e) -- luacheck: ignore diff --git a/examples/serialization/init.lua b/examples/serialization/init.lua new file mode 100644 index 0000000..2b5e05d --- /dev/null +++ b/examples/serialization/init.lua @@ -0,0 +1,69 @@ +local Concord = require("concord") + +local function display(t) + print("Table: " ..tostring(t)) + for key, value in pairs(t) do + if type(value) == "table" then + display(value) + else + print(key, value) + end + end +end + +local test_component_1 = Concord.component(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 { + x = self.x, + y = self.y, + } +end + +function test_component_1:deserialize(data) + self.x = data.x or 0 + self.y = data.y or 0 +end + +local test_component_2 = Concord.component(function(e, foo) + e.foo = foo +end) +Concord.components.register("test_component_2", test_component_2) + +function test_component_2:serialize() + return { + foo = self.foo + } +end + +function test_component_2:deserialize(data) + self.foo = data.foo +end + +-- Test worlds +local world_1 = Concord.world() +local world_2 = Concord.world() + +-- Test Entity +Concord.entity(world_1) +:give(test_component_1, 100, 50) +:give(test_component_2, "Hello World!") + +-- Serialize world +local data = world_1:serialize() + +-- Deserialize world +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] + +print(test_comp_1.x, test_comp_1.y) +print(test_comp_2.foo) \ No newline at end of file diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..7cfd88a --- /dev/null +++ b/main.lua @@ -0,0 +1 @@ +require("examples.serialization") \ No newline at end of file From 55ae5fd987aaac4cab76b84c391b1d6f62382d9a Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 4 Jan 2020 13:40:18 +0100 Subject: [PATCH 049/113] Add helper functions to container. Allow name to be gotten --- concord/assemblage.lua | 13 +++++++++++++ concord/assemblages.lua | 16 ++++++++++++++++ concord/component.lua | 4 ++++ concord/components.lua | 7 ++++++- concord/system.lua | 16 +++++++++++++++- concord/systems.lua | 15 +++++++++++++++ concord/world.lua | 13 +++++++++++++ concord/worlds.lua | 15 +++++++++++++++ 8 files changed, 97 insertions(+), 2 deletions(-) diff --git a/concord/assemblage.lua b/concord/assemblage.lua index f989a63..e6125b7 100644 --- a/concord/assemblage.lua +++ b/concord/assemblage.lua @@ -14,6 +14,7 @@ function Assemblage.new(assemble) local assemblage = setmetatable({ __assemble = assemble, + __name = nil, __isAssemblage = true, }, Assemblage.__mt) @@ -30,6 +31,18 @@ function Assemblage:assemble(e, ...) return self end +--- Returns true if the Assemblage has a name. +-- @return True if the Assemblage has a name, false otherwise +function Assemblage:hasName() + return self.__name and true or false +end + +--- Returns the name of the Assemblage. +-- @return Name of the Assemblage +function Assemblage:getName() + return self.__name +end + return setmetatable(Assemblage, { __call = function(_, ...) return Assemblage.new(...) diff --git a/concord/assemblages.lua b/concord/assemblages.lua index f34e3b9..2e5b6ad 100644 --- a/concord/assemblages.lua +++ b/concord/assemblages.lua @@ -24,8 +24,24 @@ function Assemblages.register(name, assemblage) end Assemblages[name] = assemblage + assemblage.__name = name end +--- Returns true if the containter has the Assemblage with the name +-- @param name Name of the Assemblage to check +-- @return True if the containter has the Assemblage with the name, false otherwise +function Assemblages.has(name) + return Assemblages[name] and true or false +end + +--- Returns the Assemblage with the name +-- @param name Name of the Assemblage to get +-- @return Assemblage with the name +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) diff --git a/concord/component.lua b/concord/component.lua index eece914..202e873 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -63,10 +63,14 @@ function Component:__initialize(...) return component end +--- Returns true if the Component has a name. +-- @return True if the Component has a name, false otherwise function Component:hasName() return self.__name and true or false end +--- Returns the name of the Component. +-- @return Name of the Component function Component:getName() return self.__name end diff --git a/concord/components.lua b/concord/components.lua index 522cf78..e67ec23 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -27,15 +27,20 @@ function Components.register(name, componentClass) componentClass.__name = name end +--- Returns true if the containter has the ComponentClass with the name +-- @param name Name of the ComponentClass to check +-- @return True if the containter has the ComponentClass with the name, false otherwise function Components.has(name) return Components[name] and true or false end +--- Returns the ComponentClass with the name +-- @param name Name of the ComponentClass to get +-- @return ComponentClass with the name function Components.get(name) return Components[name] end - return setmetatable(Components, { __index = function(_, name) error("Attempt to index ComponentClass '"..tostring(name).."' that does not exist / was not registered", 2) diff --git a/concord/system.lua b/concord/system.lua index 589d8ac..8eeb12d 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -53,8 +53,10 @@ System.mt = { -- @return A new SystemClass function System.new(...) local systemClass = setmetatable({ - __isSystemClass = true, __filter = {...}, + + __name = nil, + __isSystemClass = true, }, System.mt) systemClass.__index = systemClass @@ -178,6 +180,18 @@ function System:getWorld() return self.__world end +--- Returns true if the System has a name. +-- @return True if the System has a name, false otherwise +function System:hasName() + return self.__name and true or false +end + +--- Returns the name of the System. +-- @return Name of the System +function System:getName() + return self.__name +end + --- Callback for system initialization. -- @param world The World the System was added to function System:init(world) -- luacheck: ignore diff --git a/concord/systems.lua b/concord/systems.lua index 81e6b2e..4781f1d 100644 --- a/concord/systems.lua +++ b/concord/systems.lua @@ -24,6 +24,21 @@ function Systems.register(name, systemClass) end Systems[name] = systemClass + systemClass.__name = name +end + +--- Returns true if the containter has the SystemClass with the name +-- @param name Name of the SystemClass to check +-- @return True if the containter has the SystemClass with the name, false otherwise +function Systems.has(name) + return Systems[name] and true or false +end + +--- Returns the SystemClass with the name +-- @param name Name of the SystemClass to get +-- @return SystemClass with the name +function Systems.get(name) + return Systems[name] end return setmetatable(Systems, { diff --git a/concord/world.lua b/concord/world.lua index 18520cd..4461da0 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -33,6 +33,7 @@ function World.new() __systemLookup = {}, + __name = nil, __isWorld = true, }, World.__mt) @@ -316,6 +317,18 @@ function World:deserialize(data, append) self:__flush() end +--- Returns true if the World has a name. +-- @return True if the World has a name, false otherwise +function World:hasName() + return self.__name and true or false +end + +--- Returns the name of the World. +-- @return Name of the World +function World:getName() + return self.__name +end + --- Callback for when an Entity is added to the World. -- @param e The Entity that was added function World:onEntityAdded(e) -- luacheck: ignore diff --git a/concord/worlds.lua b/concord/worlds.lua index 0ec2574..9ac18ef 100644 --- a/concord/worlds.lua +++ b/concord/worlds.lua @@ -24,6 +24,21 @@ function Worlds.register(name, world) end Worlds[name] = world + world.__name = name +end + +--- Returns true if the containter has the World with the name +-- @param name Name of the World to check +-- @return True if the containter has the World with the name, false otherwise +function Worlds.has(name) + return Worlds[name] and true or false +end + +--- Returns the World with the name +-- @param name Name of the World to get +-- @return World with the name +function Worlds.get(name) + return Worlds[name] end return setmetatable(Worlds, { From f392dc6f07cded54433dbf0e2588e5a47beb8593 Mon Sep 17 00:00:00 2001 From: tesselode Date: Sat, 4 Jan 2020 09:52:50 -0500 Subject: [PATCH 050/113] fix typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b8b7fc..abc57e1 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ For example. If you want to get a specific Component from an Entity, you'd do `C When ComponentClasses or SystemClasses are required it will be written clearly in the Documentation. #### Containers -Since you'll be defining or creating lots of Components, Systems, Worlds and Assemblages Concord adds container tables for each of them so that they are easily accessible. +Since you'll be defining or creating lots of Components, Systems, Worlds and Assemblages, Concord adds container tables for each of them so that they are easily accessible. These containers can be accessed through ```lua @@ -303,7 +303,7 @@ myEntity:destroy() ### Systems -Systems are definded as a SystemClass. Concord will automatically create an instance of a System when it is needed. +Systems are defined as a SystemClass. Concord will automatically create an instance of a System when it is needed. Systems get access to Entities through `pools`. They are created using a filter. Systems can have multiple pools. From a65f88dd5ed1f5741b10b9e12674cf06b93df4b2 Mon Sep 17 00:00:00 2001 From: Andrew Minnich Date: Sat, 4 Jan 2020 10:31:05 -0500 Subject: [PATCH 051/113] make the docs a little nicer main changes: - add parameter and return types where applicable - use @module and @classmod tags at the top of files - remove some redundant descriptions of return values, especially for functions that return a boolean recommended next steps: - more consistent grammar - add links to classes and functions in descriptions where appropriate - be consistent about naming Systems vs. SystemClasses and Components vs. ComponentClasses --- concord/assemblage.lua | 19 +- concord/assemblages.lua | 22 +- concord/component.lua | 21 +- concord/components.lua | 22 +- concord/entity.lua | 52 ++-- concord/init.lua | 3 +- concord/list.lua | 18 +- concord/pool.lua | 28 +- concord/system.lua | 49 ++-- concord/systems.lua | 16 +- concord/type.lua | 14 +- concord/world.lua | 54 ++-- concord/worlds.lua | 10 +- config.ld | 3 +- docs/classes/Assemblage.html | 204 +++++++++++++ docs/classes/Component.html | 170 +++++++++++ .../entity.html => classes/Entity.html} | 114 ++++---- docs/{modules/list.html => classes/List.html} | 227 ++++++++------- docs/{modules/pool.html => classes/Pool.html} | 177 +++++------- .../system.html => classes/System.html} | 270 +++++++++--------- .../world.html => classes/World.html} | 174 ++++++----- docs/index.html | 117 ++++---- docs/modules/{init.html => Concord.html} | 55 ++-- docs/modules/assemblage.html | 147 ---------- docs/modules/assemblages.html | 102 +++++-- docs/modules/component.html | 166 ----------- docs/modules/components.html | 102 +++++-- docs/modules/systems.html | 101 +++++-- docs/modules/type.html | 48 ++-- docs/modules/utils.html | 27 +- docs/modules/worlds.html | 89 +++++- 31 files changed, 1474 insertions(+), 1147 deletions(-) create mode 100644 docs/classes/Assemblage.html create mode 100644 docs/classes/Component.html rename docs/{modules/entity.html => classes/Entity.html} (67%) rename docs/{modules/list.html => classes/List.html} (68%) rename docs/{modules/pool.html => classes/Pool.html} (61%) rename docs/{modules/system.html => classes/System.html} (55%) rename docs/{modules/world.html => classes/World.html} (65%) rename docs/modules/{init.html => Concord.html} (67%) delete mode 100644 docs/modules/assemblage.html delete mode 100644 docs/modules/component.html diff --git a/concord/assemblage.lua b/concord/assemblage.lua index e6125b7..b90fb36 100644 --- a/concord/assemblage.lua +++ b/concord/assemblage.lua @@ -1,6 +1,5 @@ ---- Assemblage --- An Assemblage is a function that 'makes' an entity something. --- It does this by :give'ing or :ensure'ing Components, or by :assemble'ing the Entity. +--- Gives an entity a set of components. +-- @classmod Assemblage local Assemblage = {} Assemblage.__mt = { @@ -8,8 +7,8 @@ Assemblage.__mt = { } --- Creates a new Assemblage. --- @param assemble Function that assembles an Entity --- @return 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, @@ -22,9 +21,9 @@ function Assemblage.new(assemble) end --- Assembles an Entity. --- @param e Entity to assemble --- @param ... Varargs to pass to the assemble function --- @ return self +-- @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, ...) @@ -32,13 +31,13 @@ function Assemblage:assemble(e, ...) end --- Returns true if the Assemblage has a name. --- @return True if the Assemblage has a name, false otherwise +-- @treturn boolean function Assemblage:hasName() return self.__name and true or false end --- Returns the name of the Assemblage. --- @return Name of the Assemblage +-- @treturn string function Assemblage:getName() return self.__name end diff --git a/concord/assemblages.lua b/concord/assemblages.lua index 2e5b6ad..58731a8 100644 --- a/concord/assemblages.lua +++ b/concord/assemblages.lua @@ -1,5 +1,5 @@ ---- Assemblages --- Container for registered Assemblages +--- A container for registered @{Assemblage}s +-- @module Assemblages local PATH = (...):gsub('%.[^%.]+$', '') @@ -8,8 +8,8 @@ local Type = require(PATH..".type") local Assemblages = {} --- Registers an Assemblage. --- @param name Name to register under --- @param assemblage Assemblage to register +-- @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) @@ -27,16 +27,16 @@ function Assemblages.register(name, assemblage) assemblage.__name = name end ---- Returns true if the containter has the Assemblage with the name --- @param name Name of the Assemblage to check --- @return True if the containter has the Assemblage with the name, false otherwise +--- 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 name --- @param name Name of the Assemblage to get --- @return Assemblage with the name +--- 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 @@ -46,4 +46,4 @@ return setmetatable(Assemblages, { __index = function(_, name) error("Attempt to index assemblage '"..tostring(name).."' that does not exist / was not registered", 2) end -}) \ No newline at end of file +}) diff --git a/concord/component.lua b/concord/component.lua index 202e873..1b391ee 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -1,6 +1,5 @@ ---- Component --- A Component is a pure data container. --- A Component is contained by a single entity. +--- A pure data container that is contained by a single entity. +-- @classmod Component local Component = {} Component.__mt = { @@ -8,8 +7,8 @@ Component.__mt = { } --- Creates a new ComponentClass. --- @param populate Function that populates a Component with values --- @return A new ComponentClass +-- @tparam function populate Function that populates a Component with values +-- @treturn Component A new ComponentClass function Component.new(populate) if (type(populate) ~= "function" and type(populate) ~= "nil") then error("bad argument #1 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) @@ -29,7 +28,7 @@ function Component.new(populate) return componentClass end ---- Internal: Populates a Component with values +-- Internal: Populates a Component with values function Component:__populate() -- luacheck: ignore end @@ -39,7 +38,7 @@ end function Component:deserialize(data) -- luacheck: ignore end ---- Internal: Creates a new Component. +-- Internal: Creates a new Component. -- @return A new Component function Component:__new() local component = setmetatable({ @@ -52,7 +51,7 @@ function Component:__new() return component end ---- Internal: Creates and populates a new Component. +-- Internal: Creates and populates a new Component. -- @param ... Varargs passed to the populate function -- @return A new populated Component function Component:__initialize(...) @@ -64,13 +63,13 @@ function Component:__initialize(...) end --- Returns true if the Component has a name. --- @return True if the Component has a name, false otherwise +-- @treturn boolean function Component:hasName() return self.__name and true or false end --- Returns the name of the Component. --- @return Name of the Component +-- @treturn string function Component:getName() return self.__name end @@ -79,4 +78,4 @@ return setmetatable(Component, { __call = function(_, ...) return Component.new(...) end, -}) \ No newline at end of file +}) diff --git a/concord/components.lua b/concord/components.lua index e67ec23..d7063da 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -1,5 +1,5 @@ ---- Components --- Container for registered ComponentClasss +--- Container for registered ComponentClasses +-- @module Components local PATH = (...):gsub('%.[^%.]+$', '') @@ -8,8 +8,8 @@ local Type = require(PATH..".type") local Components = {} --- Registers a ComponentClass. --- @param name Name to register under --- @param componentClass ComponentClass to register +-- @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) @@ -27,16 +27,16 @@ function Components.register(name, componentClass) componentClass.__name = name end ---- Returns true if the containter has the ComponentClass with the name --- @param name Name of the ComponentClass to check --- @return True if the containter has the ComponentClass with the name, false otherwise +--- 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 end ---- Returns the ComponentClass with the name --- @param name Name of the ComponentClass to get --- @return ComponentClass with the name +--- Returns the ComponentClass with the specified name +-- @string name Name of the ComponentClass to get +-- @treturn Component function Components.get(name) return Components[name] end @@ -45,4 +45,4 @@ return setmetatable(Components, { __index = function(_, name) error("Attempt to index ComponentClass '"..tostring(name).."' that does not exist / was not registered", 2) end -}) \ No newline at end of file +}) diff --git a/concord/entity.lua b/concord/entity.lua index 8359fd8..da446e5 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -1,7 +1,6 @@ ---- Entity --- Entities are the concrete objects that exist in your project. --- An Entity have Components and are processed by Systems. --- An Entity is contained by a maximum of 1 World. +--- An object that exists in a world. An entity +-- contains components which are processed by systems. +-- @classmod Entity local PATH = (...):gsub('%.[^%.]+$', '') @@ -14,8 +13,8 @@ Entity.__mt = { } --- Creates a new Entity. Optionally adds it to a World. --- @param world Optional World to add the entity to --- @return A new Entity +-- @tparam[opt] World world World to add the entity to +-- @treturn Entity A new Entity function Entity.new(world) if (world ~= nil and not Type.isWorld(world)) then error("bad argument #1 to 'Entity.new' (world/nil expected, got "..type(world)..")", 2) @@ -53,9 +52,9 @@ end --- Gives an Entity a Component. -- If the Component already exists, it's overridden by this new Component --- @param componentClass ComponentClass to add an instance of --- @param ... varargs passed to the Component's populate function --- @return self +-- @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) @@ -68,9 +67,9 @@ end --- Ensures an Entity to have a Component. -- If the Component already exists, no action is taken --- @param componentClass ComponentClass to add an instance of --- @param ... varargs passed to the Component's populate function --- @return self +-- @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) @@ -86,8 +85,8 @@ function Entity:ensure(componentClass, ...) end --- Removes a Component from an Entity. --- @param componentClass ComponentClass of the Component to remove --- @return self +-- @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)..")") @@ -99,8 +98,9 @@ function Entity:remove(componentClass) end --- Assembles an Entity. --- @param assemblage Assemblage to assemble with --- @param ... Varargs to pass to the Assemblage's assemble function. +-- @tparam Assemblage assemblage Assemblage to assemble with +-- @param ... additional arguments to pass to the Assemblage's assemble 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)..")") @@ -112,7 +112,7 @@ function Entity:assemble(assemblage, ...) end --- Destroys the Entity. --- Removes the Entity from it's World if it's in one. +-- Removes the Entity from its World if it's in one. -- @return self function Entity:destroy() if self.__world then @@ -122,7 +122,7 @@ function Entity:destroy() return self end ---- Internal: Tells the World it's in that this Entity is dirty. +-- Internal: Tells the World it's in that this Entity is dirty. -- @return self function Entity:__dirty() if self.__world then @@ -133,8 +133,8 @@ function Entity:__dirty() end --- Returns true if the Entity has a Component. --- @param componentClass ComponentClass of the Component to check --- @return True if the Entity has the Component, false otherwise +-- @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)..")") @@ -144,8 +144,8 @@ function Entity:has(componentClass) end --- Gets a Component from the Entity. --- @param componentClass ComponentClass of the Component to get --- @return The Component +-- @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)..")") @@ -157,19 +157,19 @@ end --- Returns a table of all Components the Entity has. -- Warning: Do not modify this table. -- Use Entity:give/ensure/remove instead --- @return Table of all Components the Entity has +-- @treturn table Table of all Components the Entity has function Entity:getComponents() return self.__components end --- Returns true if the Entity is in a World. --- @return True if the Entity is in a World, false otherwise +-- @treturn boolean function Entity:inWorld() return self.__world and true or false end --- Returns the World the Entity is in. --- @return The World the Entity is in. +-- @treturn World function Entity:getWorld() return self.__world end @@ -213,4 +213,4 @@ return setmetatable(Entity, { __call = function(_, ...) return Entity.new(...) end, -}) \ No newline at end of file +}) diff --git a/concord/init.lua b/concord/init.lua index 1f65695..7ff46d3 100644 --- a/concord/init.lua +++ b/concord/init.lua @@ -1,4 +1,5 @@ ---- init +--- +-- @module Concord local PATH = (...):gsub('%.init$', '') diff --git a/concord/list.lua b/concord/list.lua index 38ff949..215d1bb 100644 --- a/concord/list.lua +++ b/concord/list.lua @@ -1,5 +1,5 @@ ---- List --- Data structure that allows for fast removal at the cost of containing order. +--- Data structure that allows for fast removal at the cost of containing order. +-- @classmod List local List = {} List.__mt = { @@ -7,7 +7,7 @@ List.__mt = { } --- Creates a new List. --- @return A new List +-- @treturn List A new List function List.new() return setmetatable({ size = 0, @@ -18,7 +18,7 @@ end -- Object must be of reference type -- Object may not be the string 'size' -- @param obj Object to add --- @return self +-- @treturn List self function List:__add(obj) local size = self.size + 1 @@ -31,7 +31,7 @@ end --- Removes an object from the List. -- @param obj Object to remove --- @return self +-- @treturn List self function List:__remove(obj) local index = self[obj] if not index then return end @@ -55,7 +55,7 @@ function List:__remove(obj) end --- Clears the List completely. --- @return self +-- @treturn List self function List:__clear() for i = 1, self.size do local o = self[i] @@ -71,13 +71,13 @@ end --- Returns true if the List has the object. -- @param obj Object to check for --- @return True if the List has the object, false otherwise +-- @treturn boolean function List:has(obj) return self[obj] and true or false end --- Returns the object at an index. --- @param i Index to get from +-- @number i Index to get from -- @return Object at the index function List:get(i) return self[i] @@ -85,7 +85,7 @@ end --- Returns the index of an object in the List. -- @param obj Object to get index of --- @return index of object in the List. +-- @treturn number index of object in the List. function List:indexOf(obj) if (not self[obj]) then error("bad argument #1 to 'List:indexOf' (Object was not in List)", 2) diff --git a/concord/pool.lua b/concord/pool.lua index 4cde567..ef22aed 100644 --- a/concord/pool.lua +++ b/concord/pool.lua @@ -1,6 +1,6 @@ ---- Pool --- A Pool is used to iterate over Entities with a specific Components +--- Used to iterate over Entities with a specific Components -- A Pool contain a any amount of Entities. +-- @classmod Pool local PATH = (...):gsub('%.[^%.]+$', '') @@ -12,9 +12,9 @@ Pool.__mt = { } --- Creates a new Pool --- @param name Name for the Pool. --- @param filter Table containing the required BaseComponents --- @return The 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) @@ -27,8 +27,8 @@ function Pool.new(name, filter) end --- Checks if an Entity is eligible for the Pool. --- @param e Entity to check --- @return True if the entity is eligible, false otherwise +-- @tparam Entity e Entity to check +-- @treturn boolean function Pool:__eligible(e) for _, component in ipairs(self.__filter) do if not e[component] then @@ -39,9 +39,9 @@ function Pool:__eligible(e) return true end ---- Internal: Adds an Entity to the Pool. +-- Internal: Adds an Entity to the Pool. -- @param e Entity to add --- @return self +-- @treturn Pool self function Pool:__add(e) List.__add(self, e) self:onEntityAdded(e) @@ -49,9 +49,9 @@ function Pool:__add(e) return self end ---- Internal: Removed an Entity from the Pool. +-- Internal: Removed an Entity from the Pool. -- @param e Entity to remove --- @return self +-- @treturn Pool self function Pool:__remove(e) List.__remove(self, e) self:onEntityRemoved(e) @@ -60,7 +60,7 @@ function Pool:__remove(e) end --- Gets the name of the Pool --- @return Name of the Pool. +-- @treturn string function Pool:getName() return self.__name end @@ -73,12 +73,12 @@ function Pool:getFilter() end --- Callback for when an Entity is added to the Pool. --- @param e Entity that was added. +-- @tparam Entity e Entity that was added. function Pool:onEntityAdded(e) -- luacheck: ignore end -- Callback for when an Entity is removed from the Pool. --- @param e Entity that was removed. +-- @tparam Entity e Entity that was removed. function Pool:onEntityRemoved(e) -- luacheck: ignore end diff --git a/concord/system.lua b/concord/system.lua index 8eeb12d..6ff374e 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -1,7 +1,7 @@ ---- System --- A System iterates over Entities. From these Entities its get Components and modify them. +--- Iterates over Entities. From these Entities its get Components and modify them. -- A System contains 1 or more Pools. -- A System is contained by 1 World. +-- @classmod System local PATH = (...):gsub('%.[^%.]+$', '') @@ -50,7 +50,7 @@ System.mt = { --- Creates a new SystemClass. -- @param ... Variable amounts of filters --- @return A new SystemClass +-- @treturn System A new SystemClass function System.new(...) local systemClass = setmetatable({ __filter = {...}, @@ -70,7 +70,7 @@ function System.new(...) return systemClass end ---- Internal: Builds a Pool for the System. +-- Internal: Builds a Pool for the System. -- @param baseFilter The 'raw' Filter -- @return A new Pool function System.__buildPool(baseFilter) @@ -88,9 +88,9 @@ function System.__buildPool(baseFilter) return Pool(name, filter) end ---- Internal: Evaluates an Entity for all the System's Pools. +-- Internal: Evaluates an Entity for all the System's Pools. -- @param e The Entity to check --- @return self +-- @treturn System self function System:__evaluate(e) for _, pool in ipairs(self.__pools) do local has = pool:has(e) @@ -106,9 +106,9 @@ function System:__evaluate(e) return self end ---- Internal: Removes an Entity from the System. +-- Internal: Removes an Entity from the System. -- @param e The Entity to remove --- @return self +-- @treturn System self function System:__remove(e) for _, pool in ipairs(self.__pools) do if pool:has(e) then @@ -119,8 +119,8 @@ function System:__remove(e) return self end ---- Internal: Clears all Entities from the System. --- @return self +-- Internal: Clears all Entities from the System. +-- @treturn System self function System:__clear() for i = 1, #self.__pools do self.__pools[i]:__clear() @@ -130,7 +130,7 @@ function System:__clear() end --- Enables the System. --- @return self +-- @treturn System self function System:enable() self:setEnabled(true) @@ -138,7 +138,7 @@ function System:enable() end --- Disables the System. --- @return self +-- @treturn System self function System:disable() self:setEnabled(false) @@ -146,7 +146,7 @@ function System:disable() end --- Toggles if the System is enabled. --- @return self +-- @treturn System self function System:toggleEnabled() self:setEnabled(not self.__enabled) @@ -154,8 +154,8 @@ function System:toggleEnabled() end --- Sets if the System is enabled --- @param enable Enable --- @return self +-- @tparam boolean enable +-- @treturn System self function System:setEnabled(enable) if (not self.__enabled and enable) then self.__enabled = true @@ -169,39 +169,42 @@ function System:setEnabled(enable) end --- Returns is the System is enabled --- @return True if the System is enabled, false otherwise +-- @treturn boolean function System:isEnabled() return self.__enabled end --- Returns the World the System is in. --- @return The World the System is in +-- @treturn World function System:getWorld() return self.__world end --- Returns true if the System has a name. --- @return True if the System has a name, false otherwise +-- @treturn boolean function System:hasName() return self.__name and true or false end --- Returns the name of the System. --- @return Name of the System +-- @treturn string function System:getName() return self.__name end +--- Callbacks +-- @section Callbacks + --- Callback for system initialization. --- @param world The World the System was added to +-- @tparam World world The World the System was added to function System:init(world) -- luacheck: ignore end --- Callback for when a System is enabled. +--- Callback for when a System is enabled. function System:onEnabled() -- luacheck: ignore end --- Callback for when a System is disabled. +--- Callback for when a System is disabled. function System:onDisabled() -- luacheck: ignore end @@ -209,4 +212,4 @@ return setmetatable(System, { __call = function(_, ...) return System.new(...) end, -}) \ No newline at end of file +}) diff --git a/concord/systems.lua b/concord/systems.lua index 4781f1d..f29550d 100644 --- a/concord/systems.lua +++ b/concord/systems.lua @@ -1,5 +1,5 @@ ---- Systems --- Container for registered SystemClasses +--- Container for registered SystemClasses +-- @module Systems local PATH = (...):gsub('%.[^%.]+$', '') @@ -8,8 +8,8 @@ local Type = require(PATH..".type") local Systems = {} --- Registers a SystemClass. --- @param name Name to register under --- @param systemClass SystemClass to register +-- @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) @@ -28,14 +28,14 @@ function Systems.register(name, systemClass) end --- Returns true if the containter has the SystemClass with the name --- @param name Name of the SystemClass to check --- @return True if the containter has the SystemClass with the name, false otherwise +-- @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 --- @param name Name of the SystemClass to get +-- @tparam string name Name of the SystemClass to get -- @return SystemClass with the name function Systems.get(name) return Systems[name] @@ -45,4 +45,4 @@ return setmetatable(Systems, { __index = function(_, name) error("Attempt to index system '"..tostring(name).."' that does not exist / was not registered", 2) end -}) \ No newline at end of file +}) diff --git a/concord/type.lua b/concord/type.lua index 9a882a8..62ef0f9 100644 --- a/concord/type.lua +++ b/concord/type.lua @@ -5,49 +5,49 @@ local Type = {} --- Returns if object is an Entity. -- @param t Object to check --- @return True if object is an Entity, false otherwise +-- @treturn boolean function Type.isEntity(t) return type(t) == "table" and t.__isEntity or false end --- Returns if object is a ComponentClass. -- @param t Object to check --- @return True if object is an ComponentClass, false otherwise +-- @treturn boolean function Type.isComponentClass(t) return type(t) == "table" and t.__isComponentClass or false end --- Returns if object is a Component. -- @param t Object to check --- @return True if object is an Component, false otherwise +-- @treturn boolean function Type.isComponent(t) return type(t) == "table" and t.__isComponent or false end --- Returns if object is a SystemClass. -- @param t Object to check --- @return True if object is an SystemClass, false otherwise +-- @treturn boolean function Type.isSystemClass(t) return type(t) == "table" and t.__isSystemClass or false end --- Returns if object is a System. -- @param t Object to check --- @return True if object is an System, false otherwise +-- @treturn boolean function Type.isSystem(t) return type(t) == "table" and t.__isSystem or false end --- Returns if object is a World. -- @param t Object to check --- @return True if object is an World, false otherwise +-- @treturn boolean 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 --- @return True if object is an Assemblage, false otherwise +-- @treturn boolean function Type.isAssemblage(t) return type(t) == "table" and t.__isAssemblage or false end diff --git a/concord/world.lua b/concord/world.lua index 4461da0..e196300 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -1,8 +1,8 @@ ---- World --- A World is a collection of Systems and Entities --- A world emits to let Systems iterate --- A World contains any amount of Systems --- A World contains any amount of Entities +--- A collection of Systems and Entities. +-- A world emits to let Systems iterate. +-- A World contains any amount of Systems. +-- A World contains any amount of Entities. +-- @classmod World local PATH = (...):gsub('%.[^%.]+$', '') @@ -19,7 +19,7 @@ World.__mt = { } --- Creates a new World. --- @return The new World +-- @treturn World The new World function World.new() local world = setmetatable({ __entities = List(), @@ -48,8 +48,8 @@ function World.new() end --- Adds an Entity to the World. --- @param e Entity to add --- @return self +-- @tparam Entity e Entity to add +-- @treturn World self function World:addEntity(e) if not Type.isEntity(e) then error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) @@ -66,8 +66,8 @@ function World:addEntity(e) end --- Removes an Entity from the World. --- @param e Entity to remove --- @return self +-- @tparam Entity e Entity to remove +-- @treturn World self function World:removeEntity(e) if not Type.isEntity(e) then error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) @@ -78,7 +78,7 @@ function World:removeEntity(e) return self end ---- Internal: Marks an Entity as dirty. +-- Internal: Marks an Entity as dirty. -- @param e Entity to mark as dirty function World:__dirtyEntity(e) if not self.__dirty:has(e) then @@ -86,9 +86,9 @@ function World:__dirtyEntity(e) end end ---- Internal: Flushes all changes to Entities. +-- Internal: Flushes all changes to Entities. -- This processes all entities. Adding and removing entities, as well as reevaluating dirty entities. --- @return self +-- @treturn World self function World:__flush() -- Early out if (self.__added.size == 0 and self.__removed.size == 0 and self.__dirty.size == 0) then @@ -155,8 +155,8 @@ local blacklistedSystemFunctions = { -- Callbacks are registered automatically -- Entities added before are added to the System retroactively -- @see World:emit --- @param systemClass SystemClass of System to add --- @return self +-- @tparam System systemClass SystemClass of System to add +-- @treturn World self function World:addSystem(systemClass) if (not Type.isSystemClass(systemClass)) then error("bad argument #1 to 'World:addSystems' (SystemClass expected, got "..type(systemClass)..")", 2) @@ -202,7 +202,7 @@ end -- @see World:addSystem -- @see World:emit -- @param ... SystemClasses of Systems to add --- @return self +-- @treturn World self function World:addSystems(...) for i = 1, select("#", ...) do local systemClass = select(i, ...) @@ -214,8 +214,8 @@ function World:addSystems(...) end --- Returns if the World has a System. --- @param systemClass SystemClass of System to check for --- @return True if World has System, false otherwise +-- @tparam System systemClass SystemClass of System to check for +-- @treturn boolean function World:hasSystem(systemClass) if not Type.isSystemClass(systemClass) then error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) @@ -225,8 +225,8 @@ function World:hasSystem(systemClass) end --- Gets a System from the World. --- @param systemClass SystemClass of System to get --- @return System to get +-- @tparam System systemClass SystemClass of System to get +-- @treturn System System to get function World:getSystem(systemClass) if not Type.isSystemClass(systemClass) then error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) @@ -237,9 +237,9 @@ end --- Emits a callback in the World. -- Calls all functions with the functionName of added Systems --- @param functionName Name of functions to call. +-- @string functionName Name of functions to call. -- @param ... Parameters passed to System's functions --- @return self +-- @treturn World self function World:emit(functionName, ...) if not functionName or type(functionName) ~= "string" then error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") @@ -263,7 +263,7 @@ function World:emit(functionName, ...) end --- Removes all entities from the World --- @return self +-- @treturn World self function World:clear() for i = 1, self.__entities.size do self:removeEntity(self.__entities[i]) @@ -318,24 +318,24 @@ function World:deserialize(data, append) end --- Returns true if the World has a name. --- @return True if the World has a name, false otherwise +-- @treturn boolean function World:hasName() return self.__name and true or false end --- Returns the name of the World. --- @return Name of the World +-- @treturn string function World:getName() return self.__name end --- Callback for when an Entity is added to the World. --- @param e The Entity that was added +-- @tparam Entity e The Entity that was added function World:onEntityAdded(e) -- luacheck: ignore end --- Callback for when an Entity is removed from the World. --- @param e The Entity that was removed +-- @tparam Entity e The Entity that was removed function World:onEntityRemoved(e) -- luacheck: ignore end diff --git a/concord/worlds.lua b/concord/worlds.lua index 9ac18ef..6feba44 100644 --- a/concord/worlds.lua +++ b/concord/worlds.lua @@ -8,7 +8,7 @@ local Type = require(PATH..".type") local Worlds = {} --- Registers a World. --- @param name Name to register under +-- @tparam string name Name to register under -- @param world World to register function Worlds.register(name, world) if (type(name) ~= "string") then @@ -28,14 +28,14 @@ function Worlds.register(name, world) end --- Returns true if the containter has the World with the name --- @param name Name of the World to check --- @return True if the containter has the World with the name, false otherwise +-- @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 --- @param name Name of the World to get +-- @tparam string name Name of the World to get -- @return World with the name function Worlds.get(name) return Worlds[name] @@ -45,4 +45,4 @@ return setmetatable(Worlds, { __index = function(_, name) error("Attempt to index world '"..tostring(name).."' that does not exist / was not registered", 2) end -}) \ No newline at end of file +}) diff --git a/config.ld b/config.ld index fed6e35..9b7ea55 100644 --- a/config.ld +++ b/config.ld @@ -1,3 +1,4 @@ project = 'Concord' description = 'A feature-complete ECS library' -file = {'concord', exclude = {}} \ No newline at end of file +file = {'concord', exclude = {}} +dir = 'docs' diff --git a/docs/classes/Assemblage.html b/docs/classes/Assemblage.html new file mode 100644 index 0000000..c61009b --- /dev/null +++ b/docs/classes/Assemblage.html @@ -0,0 +1,204 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class Assemblage

+

Gives an entity a set of components.

+

+ + +

Methods

+ + + + + + + + + + + + + + + + + +
Assemblage:new (assemble)Creates a new Assemblage.
Assemblage:assemble (e, ...)Assembles an Entity.
Assemblage:hasName ()Returns true if the Assemblage has a name.
Assemblage:getName ()Returns the name of the Assemblage.
+ +
+
+ + +

Methods

+ +
+
+ + Assemblage:new (assemble) +
+
+ Creates a new Assemblage. + + +

Parameters:

+
    +
  • assemble + function + Function that assembles an Entity +
  • +
+ +

Returns:

+
    + + Assemblage + A new assemblage +
+ + + + +
+
+ + Assemblage:assemble (e, ...) +
+
+ Assembles an Entity. + + +

Parameters:

+
    +
  • e + Entity + Entity to assemble +
  • +
  • ... + additional arguments to pass to the assemble function +
  • +
+ +

Returns:

+
    + + Assemblage + self +
+ + + + +
+
+ + Assemblage:hasName () +
+
+ Returns true if the Assemblage has a name. + + + +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + Assemblage:getName () +
+
+ Returns the name of the Assemblage. + + + +

Returns:

+
    + + string + +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 10:27:07 +
+
+ + diff --git a/docs/classes/Component.html b/docs/classes/Component.html new file mode 100644 index 0000000..ad4f3e2 --- /dev/null +++ b/docs/classes/Component.html @@ -0,0 +1,170 @@ + + + + + Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class Component

+

A pure data container that is contained by a single entity.

+

+ + +

Methods

+ + + + + + + + + + + + + +
Component:new (populate)Creates a new ComponentClass.
Component:hasName ()Returns true if the Component has a name.
Component:getName ()Returns the name of the Component.
+ +
+
+ + +

Methods

+ +
+
+ + Component:new (populate) +
+
+ Creates a new ComponentClass. + + +

Parameters:

+
    +
  • populate + function + Function that populates a Component with values +
  • +
+ +

Returns:

+
    + + Component + A new ComponentClass +
+ + + + +
+
+ + Component:hasName () +
+
+ Returns true if the Component has a name. + + + +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + Component:getName () +
+
+ Returns the name of the Component. + + + +

Returns:

+
    + + string + +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-01-04 10:27:07 +
+
+ + diff --git a/docs/modules/entity.html b/docs/classes/Entity.html similarity index 67% rename from docs/modules/entity.html rename to docs/classes/Entity.html index 4350a6f..5ecd569 100644 --- a/docs/modules/entity.html +++ b/docs/classes/Entity.html @@ -32,25 +32,28 @@

Contents

+

Classes

+

Modules

@@ -58,18 +61,17 @@
-

Module entity

-

Entity - Entities are the concrete objects that exist in your project.

-

- An Entity have Components and are processed by Systems. - An Entity is contained by a maximum of 1 World.

+

Class Entity

+

An object that exists in a world.

+

An entity + contains components which are processed by systems. +

-

Functions

+

Methods

- + @@ -93,10 +95,6 @@ - - - - @@ -122,12 +120,12 @@
-

Functions

+

Methods

- - Entity.new (world) + + Entity:new ([world])
Creates a new Entity. Optionally adds it to a World. @@ -136,13 +134,16 @@

Parameters:

  • world - Optional World to add the entity to + World + World to add the entity to + (optional)

Returns:

    + Entity A new Entity
@@ -162,16 +163,18 @@

Parameters:

  • componentClass + Component ComponentClass to add an instance of
  • ... - varargs passed to the Component's populate function + additional arguments to pass to the Component's populate function

Returns:

    + Entity self
@@ -191,16 +194,18 @@

Parameters:

  • componentClass + Component ComponentClass to add an instance of
  • ... - varargs passed to the Component's populate function + additional arguments to pass to the Component's populate function

Returns:

    + Entity self
@@ -219,6 +224,7 @@

Parameters:

  • componentClass + Component ComponentClass of the Component to remove
@@ -226,6 +232,7 @@

Returns:

    + Entity self
@@ -244,13 +251,20 @@

Parameters:

  • assemblage + Assemblage Assemblage to assemble with
  • ... - Varargs to pass to the Assemblage's assemble function. + additional arguments to pass to the Assemblage's assemble function.
+

Returns:

+
    + + Entity + self +
@@ -262,26 +276,7 @@
Destroys the Entity. - Removes the Entity from it's World if it's in one. - - - -

Returns:

-
    - - self -
- - - - -
-
- - Entity:__dirty () -
-
- Internal: Tells the World it's in that this Entity is dirty. + Removes the Entity from its World if it's in one. @@ -306,6 +301,7 @@

Parameters:

  • componentClass + Component ComponentClass of the Component to check
@@ -313,7 +309,8 @@

Returns:

    - True if the Entity has the Component, false otherwise + boolean +
@@ -331,6 +328,7 @@

Parameters:

  • componentClass + Component ComponentClass of the Component to get
@@ -338,7 +336,8 @@

Returns:

    - The Component + table +
@@ -359,6 +358,7 @@

Returns:

    + table Table of all Components the Entity has
@@ -378,7 +378,8 @@

Returns:

    - True if the Entity is in a World, false otherwise + boolean +
@@ -397,7 +398,8 @@

Returns:

    - The World the Entity is in. + World +
@@ -411,7 +413,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/list.html b/docs/classes/List.html similarity index 68% rename from docs/modules/list.html rename to docs/classes/List.html index 757c1d3..b6becb6 100644 --- a/docs/modules/list.html +++ b/docs/classes/List.html @@ -32,25 +32,29 @@

Contents

+

Classes

+

Modules

@@ -58,31 +62,18 @@
-

Module list

-

List - Data structure that allows for fast removal at the cost of containing order.

+

Class List

+

Data structure that allows for fast removal at the cost of containing order.

-

Functions

+

Methods

Entity.new (world)Entity:new ([world]) Creates a new Entity.
Destroys the Entity.
Entity:__dirty ()Internal: Tells the World it's in that this Entity is dirty.
Entity:has (componentClass) Returns true if the Entity has a Component.
- + - - - - - - - - - - - - @@ -95,17 +86,32 @@
List.new ()List:new () Creates a new List.
List:__add (obj)Adds an object to the List.
List:__remove (obj)Removes an object from the List.
List:__clear ()Clears the List completely.
List:has (obj) Returns true if the List has the object.
Returns the index of an object in the List.
+

Metamethods

+ + + + + + + + + + + + + +
List:__add (obj)Adds an object to the List.
List:__remove (obj)Removes an object from the List.
List:__clear ()Clears the List completely.


-

Functions

+

Methods

- - List.new () + + List:new ()
Creates a new List. @@ -115,83 +121,13 @@

Returns:

    + List A new List
-
-
- - List:__add (obj) -
-
- Adds an object to the List. - Object must be of reference type - Object may not be the string 'size' - - -

Parameters:

-
    -
  • obj - Object to add -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - List:__remove (obj) -
-
- Removes an object from the List. - - -

Parameters:

-
    -
  • obj - Object to remove -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - List:__clear () -
-
- Clears the List completely. - - - -

Returns:

-
    - - self -
- - - -
@@ -211,7 +147,8 @@

Returns:

    - True if the List has the object, false otherwise + boolean +
@@ -229,6 +166,7 @@

Parameters:

  • i + number Index to get from
@@ -261,12 +199,91 @@

Returns:

    + number index of object in the List.
+ +
+

Metamethods

+ +
+
+ + List:__add (obj) +
+
+ Adds an object to the List. + Object must be of reference type + Object may not be the string 'size' + + +

Parameters:

+
    +
  • obj + Object to add +
  • +
+ +

Returns:

+
    + + List + self +
+ + + + +
+
+ + List:__remove (obj) +
+
+ Removes an object from the List. + + +

Parameters:

+
    +
  • obj + Object to remove +
  • +
+ +

Returns:

+
    + + List + self +
+ + + + +
+
+ + List:__clear () +
+
+ Clears the List completely. + + + +

Returns:

+
    + + List + self +
+ + + +
@@ -275,7 +292,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/pool.html b/docs/classes/Pool.html similarity index 61% rename from docs/modules/pool.html rename to docs/classes/Pool.html index ed53743..184ad8c 100644 --- a/docs/modules/pool.html +++ b/docs/classes/Pool.html @@ -32,25 +32,29 @@

Contents

+

Classes

+

Modules

@@ -58,32 +62,19 @@
-

Module pool

-

Pool - A Pool is used to iterate over Entities with a specific Components +

Class Pool

+

Used to iterate over Entities with a specific Components A Pool contain a any amount of Entities.

-

Functions

+

Methods

- + - - - - - - - - - - - - @@ -96,17 +87,24 @@
Pool.new (name, filter)Pool:new (name, filter) Creates a new Pool
Pool:__eligible (e)Checks if an Entity is eligible for the Pool.
Pool:__add (e)Internal: Adds an Entity to the Pool.
Pool:__remove (e)Internal: Removed an Entity from the Pool.
Pool:getName () Gets the name of the Pool
Callback for when an Entity is added to the Pool.
+

Metamethods

+ + + + + +
Pool:__eligible (e)Checks if an Entity is eligible for the Pool.


-

Functions

+

Methods

- - Pool.new (name, filter) + + Pool:new (name, filter)
Creates a new Pool @@ -115,9 +113,11 @@

Parameters:

  • name + string Name for the Pool.
  • filter + table Table containing the required BaseComponents
@@ -125,87 +125,13 @@

Returns:

    + Pool The new Pool
-
-
- - Pool:__eligible (e) -
-
- Checks if an Entity is eligible for the Pool. - - -

Parameters:

-
    -
  • e - Entity to check -
  • -
- -

Returns:

-
    - - True if the entity is eligible, false otherwise -
- - - - -
-
- - Pool:__add (e) -
-
- Internal: Adds an Entity to the Pool. - - -

Parameters:

-
    -
  • e - Entity to add -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - Pool:__remove (e) -
-
- Internal: Removed an Entity from the Pool. - - -

Parameters:

-
    -
  • e - Entity to remove -
  • -
- -

Returns:

-
    - - self -
- - - -
@@ -219,7 +145,8 @@

Returns:

    - Name of the Pool. + string +
@@ -257,6 +184,7 @@

Parameters:

  • e + Entity Entity that was added.
@@ -265,6 +193,37 @@ + +
+

Metamethods

+ +
+
+ + Pool:__eligible (e) +
+
+ Checks if an Entity is eligible for the Pool. + + +

Parameters:

+
    +
  • e + Entity + Entity to check +
  • +
+ +

Returns:

+
    + + boolean + +
+ + + +
@@ -273,7 +232,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/system.html b/docs/classes/System.html similarity index 55% rename from docs/modules/system.html rename to docs/classes/System.html index 8011319..914bd9b 100644 --- a/docs/modules/system.html +++ b/docs/classes/System.html @@ -32,25 +32,29 @@

Contents

+

Classes

+

Modules

@@ -58,37 +62,21 @@
-

Module system

-

System - A System iterates over Entities.

+

Class System

+

Iterates over Entities.

From these Entities its get Components and modify them. A System contains 1 or more Pools. - A System is contained by 1 World.

+ A System is contained by 1 World. +

-

Functions

+

Methods

- + - - - - - - - - - - - - - - - - @@ -97,7 +85,7 @@ - + @@ -112,22 +100,41 @@ + + + + + + + + +
System.new (...)System:new (...) Creates a new SystemClass.
System.__buildPool (baseFilter)Internal: Builds a Pool for the System.
System:__evaluate (e)Internal: Evaluates an Entity for all the System's Pools.
System:__remove (e)Internal: Removes an Entity from the System.
System:clear ()Internal: Clears all Entities from the System.
System:enable () Enables the System.
Disables the System.
System:toggleEnable ()System:toggleEnabled () Toggles if the System is enabled.
System:getWorld () Returns the World the System is in.
System:hasName ()Returns true if the System has a name.
System:getName ()Returns the name of the System.
+

Callbacks

+ + + + + + + + +
System:init (world) Callback for system initialization.
System:onEnabled ()Callback for when a System is enabled.
System:onDisabled ()Callback for when a System is disabled.


-

Functions

+

Methods

- - System.new (...) + + System:new (...)
Creates a new SystemClass. @@ -143,106 +150,13 @@

Returns:

    + System A new SystemClass
-
-
- - System.__buildPool (baseFilter) -
-
- Internal: Builds a Pool for the System. - - -

Parameters:

-
    -
  • baseFilter - The 'raw' Filter -
  • -
- -

Returns:

-
    - - A new Pool -
- - - - -
-
- - System:__evaluate (e) -
-
- Internal: Evaluates an Entity for all the System's Pools. - - -

Parameters:

-
    -
  • e - The Entity to check -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - System:__remove (e) -
-
- Internal: Removes an Entity from the System. - - -

Parameters:

-
    -
  • e - The Entity to remove -
  • -
- -

Returns:

-
    - - self -
- - - - -
-
- - System:clear () -
-
- Internal: Clears all Entities from the System. - - - -

Returns:

-
    - - self -
- - - -
@@ -256,6 +170,7 @@

Returns:

    + System self
@@ -275,6 +190,7 @@

Returns:

    + System self
@@ -283,8 +199,8 @@
- - System:toggleEnable () + + System:toggleEnabled ()
Toggles if the System is enabled. @@ -294,6 +210,7 @@

Returns:

    + System self
@@ -312,13 +229,15 @@

Parameters:

  • enable - Enable + boolean +

Returns:

    + System self
@@ -338,7 +257,8 @@

Returns:

    - True if the System is enabled, false otherwise + boolean +
@@ -357,13 +277,58 @@

Returns:

    - The World the System is in + World +
+
+ + System:hasName () +
+
+ Returns true if the System has a name. + + + +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + System:getName () +
+
+ Returns the name of the System. + + + +

Returns:

+
    + + string + +
+ + + + +
+
+

Callbacks

+ +
System:init (world) @@ -375,6 +340,7 @@

Parameters:

  • world + World The World the System was added to
@@ -383,6 +349,34 @@ + +
+ + System:onEnabled () +
+
+ Callback for when a System is enabled. + + + + + + + +
+
+ + System:onDisabled () +
+
+ Callback for when a System is disabled. + + + + + + +
@@ -391,7 +385,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/world.html b/docs/classes/World.html similarity index 65% rename from docs/modules/world.html rename to docs/classes/World.html index 8338c23..ce29521 100644 --- a/docs/modules/world.html +++ b/docs/classes/World.html @@ -32,25 +32,28 @@

Contents

+

Classes

+

Modules

@@ -58,19 +61,19 @@
-

Module world

-

World - A World is a collection of Systems and Entities - A world emits to let Systems iterate - A World contains any amount of Systems - A World contains any amount of Entities

-

+

Class World

+

A collection of Systems and Entities.

+

+ A world emits to let Systems iterate. + A World contains any amount of Systems. + A World contains any amount of Entities. +

-

Functions

+

Methods

- + @@ -82,14 +85,6 @@ - - - - - - - - @@ -114,6 +109,14 @@ + + + + + + + + @@ -127,12 +130,12 @@
-

Functions

+

Methods

- - World.new () + + World:new ()
Creates a new World. @@ -142,6 +145,7 @@

Returns:

    + World The new World
@@ -160,6 +164,7 @@

Parameters:

@@ -167,6 +172,7 @@

Returns:

    + World self
@@ -185,6 +191,7 @@

Parameters:

@@ -192,46 +199,7 @@

Returns:

    - self -
- - - - -
-
- - World:__dirtyEntity (e) -
-
- Internal: Marks an Entity as dirty. - - -

Parameters:

-
    -
  • e - Entity to mark as dirty -
  • -
- - - - - -
-
- - World:__flush () -
-
- Internal: Flushes all changes to Entities. - This processes all entities. Adding and removing entities, as well as reevaluating dirty entities. - - - -

Returns:

-
    - + World self
@@ -252,6 +220,7 @@

Parameters:

  • systemClass + System SystemClass of System to add
@@ -259,13 +228,14 @@

Returns:

    + World self

See also:

@@ -289,14 +259,15 @@

Returns:

    + World self

See also:

@@ -312,6 +283,7 @@

Parameters:

  • systemClass + System SystemClass of System to check for
@@ -319,7 +291,8 @@

Returns:

    - True if World has System, false otherwise + boolean +
@@ -337,6 +310,7 @@

Parameters:

  • systemClass + System SystemClass of System to get
@@ -344,6 +318,7 @@

Returns:

    + System System to get
@@ -363,6 +338,7 @@

Parameters:

  • functionName + string Name of functions to call.
  • ... @@ -373,6 +349,7 @@

    Returns:

      + World self
    @@ -392,12 +369,53 @@

    Returns:

      + World self
    +
+
+ + World:hasName () +
+
+ Returns true if the World has a name. + + + +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + World:getName () +
+
+ Returns the name of the World. + + + +

Returns:

+
    + + string + +
+ + + +
@@ -410,6 +428,7 @@

Parameters:

  • e + Entity The Entity that was added
@@ -430,6 +449,7 @@

Parameters:

  • e + Entity The Entity that was removed
@@ -446,7 +466,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/index.html b/docs/index.html index c46ade3..427d6b7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,21 +31,24 @@

Modules

+

Classes

+ @@ -57,54 +60,20 @@

Modules

World.new ()World:new () Creates a new World.
Removes an Entity from the World.
World:__dirtyEntity (e)Internal: Marks an Entity as dirty.
World:__flush ()Internal: Flushes all changes to Entities.
World:addSystem (systemClass) Adds a System to the World.
Removes all entities from the World
World:hasName ()Returns true if the World has a name.
World:getName ()Returns the name of the World.
World:onEntityAdded (e) Callback for when an Entity is added to the World.
- - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -115,14 +84,6 @@ - - - - @@ -130,12 +91,44 @@ Container for registered Worlds
assemblageAssemblage - An Assemblage is a function that 'makes' an entity something.AssemblagesA container for registered Assemblages
assemblagesAssemblages - Container for registered AssemblagesComponentsContainer for registered ComponentClasses
componentComponent - A Component is a pure data container.Concord
componentsComponents - Container for registered ComponentClasss
entityEntity - Entities are the concrete objects that exist in your project.
initinit
listList - Data structure that allows for fast removal at the cost of containing order.
poolPool - A Pool is used to iterate over Entities with a specific Components - A Pool contain a any amount of Entities.
systemSystem - A System iterates over Entities.
systemsSystems - Container for registered SystemClassesSystemsContainer for registered SystemClasses
typeutils Utils Helper module for misc operations
worldWorld - A World is a collection of Systems and Entities - A world emits to let Systems iterate - A World contains any amount of Systems - A World contains any amount of Entities
worlds
+

Classes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssemblageGives an entity a set of components.
ComponentA pure data container that is contained by a single entity.
EntityAn object that exists in a world.
ListData structure that allows for fast removal at the cost of containing order.
PoolUsed to iterate over Entities with a specific Components + A Pool contain a any amount of Entities.
SystemIterates over Entities.
WorldA collection of Systems and Entities.
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/init.html b/docs/modules/Concord.html similarity index 67% rename from docs/modules/init.html rename to docs/modules/Concord.html index b3b246d..b50ef54 100644 --- a/docs/modules/init.html +++ b/docs/modules/Concord.html @@ -38,47 +38,50 @@

Modules

+

Classes

+
-

Module init

-

init

+

Module Concord

+

Functions

- + - + - + - +
Concord.loadComponents (pathOrFiles)loadComponents (pathOrFiles) Loads ComponentClasses and puts them in the Components container.
Concord.loadSystems (pathOrFiles)loadSystems (pathOrFiles) Loads SystemClasses and puts them in the Systems container.
Concord.loadWorlds (pathOrFiles)loadWorlds (pathOrFiles) Loads Worlds and puts them in the Worlds container.
Concord.loadAssemblages (pathOrFiles)loadAssemblages (pathOrFiles) Loads Assemblages and puts them in the Assemblages container.
@@ -91,8 +94,8 @@
- - Concord.loadComponents (pathOrFiles) + + loadComponents (pathOrFiles)
Loads ComponentClasses and puts them in the Components container. @@ -113,8 +116,8 @@
- - Concord.loadSystems (pathOrFiles) + + loadSystems (pathOrFiles)
Loads SystemClasses and puts them in the Systems container. @@ -135,8 +138,8 @@
- - Concord.loadWorlds (pathOrFiles) + + loadWorlds (pathOrFiles)
Loads Worlds and puts them in the Worlds container. @@ -157,8 +160,8 @@
- - Concord.loadAssemblages (pathOrFiles) + + loadAssemblages (pathOrFiles)
Loads Assemblages and puts them in the Assemblages container. @@ -185,7 +188,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/assemblage.html b/docs/modules/assemblage.html deleted file mode 100644 index bfb4d07..0000000 --- a/docs/modules/assemblage.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Reference - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Module assemblage

-

Assemblage - An Assemblage is a function that 'makes' an entity something.

-

- It does this by :give'ing or :ensure'ing Components, or by :assemble'ing the Entity.

- - -

Functions

- - - - - - - - - -
Assemblage.new (assemble)Creates a new Assemblage.
Assemblage:assemble (e, ...)Assembles an Entity.
- -
-
- - -

Functions

- -
-
- - Assemblage.new (assemble) -
-
- Creates a new Assemblage. - - -

Parameters:

-
    -
  • assemble - Function that assembles an Entity -
  • -
- -

Returns:

-
    - - A new Assemblage -
- - - - -
-
- - Assemblage:assemble (e, ...) -
-
- Assembles an Entity. - - -

Parameters:

-
    -
  • e - Entity to assemble -
  • -
  • ... - Varargs to pass to the assemble function - @ return self -
  • -
- - - - - -
-
- - -
-
-
-generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 -
-
- - diff --git a/docs/modules/assemblages.html b/docs/modules/assemblages.html index 4aeed9c..8b4d725 100644 --- a/docs/modules/assemblages.html +++ b/docs/modules/assemblages.html @@ -38,38 +38,48 @@

Modules

+

Classes

+
-

Module assemblages

-

Assemblages - Container for registered Assemblages

+

Module Assemblages

+

A container for registered Assemblages

Functions

- + + + + + + + + +
Assemblages.register (name, assemblage)register (name, assemblage) Registers an Assemblage.
has (name)Returns true if the containter has an Assemblage with the specified name
get (name)Returns the Assemblage with the specified name

@@ -80,8 +90,8 @@
- - Assemblages.register (name, assemblage) + + register (name, assemblage)
Registers an Assemblage. @@ -90,9 +100,11 @@

Parameters:

  • name + string Name to register under
  • assemblage + Assemblage Assemblage to register
@@ -101,6 +113,60 @@ +
+
+ + has (name) +
+
+ Returns true if the containter has an Assemblage with the specified name + + +

Parameters:

+
    +
  • name + string + Name of the Assemblage to check +
  • +
+ +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + get (name) +
+
+ Returns the Assemblage with the specified name + + +

Parameters:

+
    +
  • name + string + Name of the Assemblage to get +
  • +
+ +

Returns:

+
    + + Assemblage + +
+ + + +
@@ -109,7 +175,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/component.html b/docs/modules/component.html deleted file mode 100644 index a5ce1fb..0000000 --- a/docs/modules/component.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - Reference - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Module component

-

Component - A Component is a pure data container.

-

- A Component is contained by a single entity.

- - -

Functions

- - - - - - - - - - - - - -
Component.new (populate)Creates a new ComponentClass.
Component:__populate ()Internal: Populates a Component with values
Component:__initialize (...)Internal: Creates and populates a new Component.
- -
-
- - -

Functions

- -
-
- - Component.new (populate) -
-
- Creates a new ComponentClass. - - -

Parameters:

-
    -
  • populate - Function that populates a Component with values -
  • -
- -

Returns:

-
    - - A new ComponentClass -
- - - - -
-
- - Component:__populate () -
-
- Internal: Populates a Component with values - - - - - - - -
-
- - Component:__initialize (...) -
-
- Internal: Creates and populates a new Component. - - -

Parameters:

-
    -
  • ... - Varargs passed to the populate function -
  • -
- -

Returns:

-
    - - A new populated Component -
- - - - -
-
- - -
-
-
-generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 -
-
- - diff --git a/docs/modules/components.html b/docs/modules/components.html index c9e4dd7..f97534d 100644 --- a/docs/modules/components.html +++ b/docs/modules/components.html @@ -38,38 +38,48 @@

Modules

+

Classes

+
-

Module components

-

Components - Container for registered ComponentClasss

+

Module Components

+

Container for registered ComponentClasses

Functions

- + + + + + + + + +
Components.register (name, componentClass)register (name, componentClass) Registers a ComponentClass.
has (name)Returns true if the containter has the ComponentClass with the specified name
get (name)Returns the ComponentClass with the specified name

@@ -80,8 +90,8 @@
- - Components.register (name, componentClass) + + register (name, componentClass)
Registers a ComponentClass. @@ -90,9 +100,11 @@

Parameters:

  • name + string Name to register under
  • componentClass + Component ComponentClass to register
@@ -101,6 +113,60 @@ +
+
+ + has (name) +
+
+ Returns true if the containter has the ComponentClass with the specified name + + +

Parameters:

+
    +
  • name + string + Name of the ComponentClass to check +
  • +
+ +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + get (name) +
+
+ Returns the ComponentClass with the specified name + + +

Parameters:

+
    +
  • name + string + Name of the ComponentClass to get +
  • +
+ +

Returns:

+
    + + Component + +
+ + + +
@@ -109,7 +175,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/systems.html b/docs/modules/systems.html index d6bf949..c1e22b0 100644 --- a/docs/modules/systems.html +++ b/docs/modules/systems.html @@ -38,38 +38,48 @@

Modules

+

Classes

+
-

Module systems

-

Systems - Container for registered SystemClasses

+

Module Systems

+

Container for registered SystemClasses

Functions

- + + + + + + + + +
Systems.register (name, systemClass)register (name, systemClass) Registers a SystemClass.
has (name)Returns true if the containter has the SystemClass with the name
get (name)Returns the SystemClass with the name

@@ -80,8 +90,8 @@
- - Systems.register (name, systemClass) + + register (name, systemClass)
Registers a SystemClass. @@ -90,9 +100,11 @@

Parameters:

  • name + string Name to register under
  • systemClass + System SystemClass to register
@@ -101,6 +113,59 @@ +
+
+ + has (name) +
+
+ Returns true if the containter has the SystemClass with the name + + +

Parameters:

+
    +
  • name + string + Name of the SystemClass to check +
  • +
+ +

Returns:

+
    + + boolean + +
+ + + + +
+
+ + get (name) +
+
+ Returns the SystemClass with the name + + +

Parameters:

+
    +
  • name + string + Name of the SystemClass to get +
  • +
+ +

Returns:

+
    + + SystemClass with the name +
+ + + +
@@ -109,7 +174,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/type.html b/docs/modules/type.html index 377c972..923c547 100644 --- a/docs/modules/type.html +++ b/docs/modules/type.html @@ -38,21 +38,24 @@

Modules

+

Classes

+ @@ -121,7 +124,8 @@

Returns:

    - True if object is an Entity, false otherwise + boolean +
@@ -146,7 +150,8 @@

Returns:

    - True if object is an ComponentClass, false otherwise + boolean +
@@ -171,7 +176,8 @@

Returns:

    - True if object is an Component, false otherwise + boolean +
@@ -196,7 +202,8 @@

Returns:

    - True if object is an SystemClass, false otherwise + boolean +
@@ -221,7 +228,8 @@

Returns:

    - True if object is an System, false otherwise + boolean +
@@ -246,7 +254,8 @@

Returns:

    - True if object is an World, false otherwise + boolean +
@@ -271,7 +280,8 @@

Returns:

    - True if object is an Assemblage, false otherwise + boolean +
@@ -285,7 +295,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/utils.html b/docs/modules/utils.html index 925eb00..30d8e2a 100644 --- a/docs/modules/utils.html +++ b/docs/modules/utils.html @@ -38,21 +38,24 @@

Modules

+

Classes

+ @@ -109,7 +112,7 @@
generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
diff --git a/docs/modules/worlds.html b/docs/modules/worlds.html index 74bf7c3..88489f7 100644 --- a/docs/modules/worlds.html +++ b/docs/modules/worlds.html @@ -38,21 +38,24 @@

Modules

+

Classes

+ @@ -70,6 +73,14 @@ Worlds.register (name, world) Registers a World. + + Worlds.has (name) + Returns true if the containter has the World with the name + + + Worlds.get (name) + Returns the World with the name +
@@ -90,6 +101,7 @@

Parameters:

  • name + string Name to register under
  • world @@ -101,6 +113,59 @@ + +
    + + Worlds.has (name) +
    +
    + Returns true if the containter has the World with the name + + +

    Parameters:

    +
      +
    • name + string + Name of the World to check +
    • +
    + +

    Returns:

    +
      + + boolean + +
    + + + + +
    +
    + + Worlds.get (name) +
    +
    + Returns the World with the name + + +

    Parameters:

    +
      +
    • name + string + Name of the World to get +
    • +
    + +

    Returns:

    +
      + + World with the name +
    + + + +
    @@ -109,7 +174,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 00:43:06 +Last updated 2020-01-04 10:27:07
    From 532151d901554f3e84e42cbc9ef63329a2a7401a Mon Sep 17 00:00:00 2001 From: speakk Date: Mon, 6 Jan 2020 02:56:18 +0200 Subject: [PATCH 052/113] Fix a typo in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index abc57e1..1ff674f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ local Assemblages = Concord.assemblages ## ECS Concord is an Entity Component System (ECS for short) library. This is a coding paradigm where _composition_ is used over _inheritance_. -Because of this it is easier to write more modular code. It often allowes you to combine any form of behaviour for the objects in your game (Entities). +Because of this it is easier to write more modular code. It often allows you to combine any form of behaviour for the objects in your game (Entities). As the name might suggest, ECS consists of 3 core things: Entities, Components, and Systems. A proper understanding of these is required to use Concord effectively. We'll start with the simplest one. From 91f5799e5006d2be05f3638d0030e33fe56f222b Mon Sep 17 00:00:00 2001 From: Jack Robinson Date: Thu, 9 Jan 2020 22:12:39 +1300 Subject: [PATCH 053/113] README: Fix 'Quick Example' to be runnable --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index abc57e1..d7aea42 100644 --- a/README.md +++ b/README.md @@ -536,19 +536,19 @@ function MoveSystem:update(dt) end -local DrawSystem = Concord.System({Position, Drawable}) +local DrawSystem = Concord.system({Position, Drawable}) function DrawSystem:draw() for _, e in ipairs(self.pool) do local position = e[Position] - + love.graphics.circle("fill", position.x, position.y, 5) end end -- Create the World -local world = World() +local world = Concord.world() -- Add the Systems world:addSystems(MoveSystem, DrawSystem) @@ -571,11 +571,11 @@ local entity_3 = Concord.entity(world) -- Emit the events function love.update(dt) - world:emit(dt) + world:emit("update", dt) end function love.draw() - world:draw() + world:emit("draw") end ``` From 275d54726d205164da221293a02b9137f689fa3e Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 18 Jan 2020 19:26:38 +0100 Subject: [PATCH 054/113] Allow emits within emits --- concord/world.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/concord/world.lua b/concord/world.lua index e196300..7708bb6 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -25,7 +25,8 @@ function World.new() __entities = List(), __systems = List(), - __events = {}, + __events = {}, + __emitSDepth = 0, __added = List(), __backAdded = List(), __removed = List(), __backRemoved = List(), @@ -245,6 +246,8 @@ function World:emit(functionName, ...) error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") end + self.__emitSDepth = self.__emitSDepth + 1 + local listeners = self.__events[functionName] if listeners then @@ -252,13 +255,17 @@ function World:emit(functionName, ...) local listener = listeners[i] if (listener.system.__enabled) then - self:__flush() + if (self.__emitSDepth == 0) then + self:__flush() + end listener.callback(listener.system, ...) end end end + self.__emitSDepth = self.__emitSDepth - 1 + return self end From 5dffe04b7227d9874c241056980be84ef9d91ca2 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 18 Jan 2020 19:44:09 +0100 Subject: [PATCH 055/113] Fix flushing failing --- concord/world.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/concord/world.lua b/concord/world.lua index 7708bb6..70551c0 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -246,6 +246,8 @@ function World:emit(functionName, ...) error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") end + local shouldFlush = self.__emitSDepth == 0 + self.__emitSDepth = self.__emitSDepth + 1 local listeners = self.__events[functionName] @@ -255,7 +257,7 @@ function World:emit(functionName, ...) local listener = listeners[i] if (listener.system.__enabled) then - if (self.__emitSDepth == 0) then + if (shouldFlush) then self:__flush() end From c640641b0916b2e996a4c960f6115910b3b1d63c Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 07:45:25 -0300 Subject: [PATCH 056/113] 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) From 16e111176ee0be9d4f3258751835155c2e9ea2bc Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 14:55:11 -0300 Subject: [PATCH 057/113] Added Utils.loadNamespace Deleted the previous Concord.loadComponents/Systems/Worlds --- concord/init.lua | 76 +---------------------------------------------- concord/utils.lua | 53 +++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 78 deletions(-) diff --git a/concord/init.lua b/concord/init.lua index 1653c46..cf5f2f2 100644 --- a/concord/init.lua +++ b/concord/init.lua @@ -33,84 +33,10 @@ local Concord = { } Concord.entity = require(PATH..".entity") - Concord.component = require(PATH..".component") Concord.components = require(PATH..".components") - Concord.system = require(PATH..".system") - Concord.world = require(PATH..".world") - -local function load(pathOrFiles, namespace) - if (type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table") then - error("bad argument #1 to 'load' (string/table of strings expected, got "..type(pathOrFiles)..")", 3) - end - - if (type(pathOrFiles) == "string") then - local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore - if (info == nil or info.type ~= "directory") then - error("bad argument #1 to 'load' (path '"..pathOrFiles.."' not found)", 3) - end - - local files = love.filesystem.getDirectoryItems(pathOrFiles) - - for _, file in ipairs(files) do - local name = file:sub(1, #file - 4) - local path = pathOrFiles.."."..name - - local value = require(path) - if namespace then namespace[name] = value end - end - elseif (type(pathOrFiles == "table")) then - for _, path in ipairs(pathOrFiles) do - if (type(path) ~= "string") then - error("bad argument #2 to 'load' (string/table of strings expected, got table containing "..type(path)..")", 3) -- luacheck: ignore - end - - local name = path - - local dotIndex, slashIndex = path:match("^.*()%."), path:match("^.*()%/") - if (dotIndex or slashIndex) then - name = path:sub((dotIndex or slashIndex) + 1) - end - - 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, 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, 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) - return load(pathOrFiles, {}) -end - +Concord.utils = require(PATH..".utils") return Concord diff --git a/concord/utils.lua b/concord/utils.lua index 6372b5b..68ae22b 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -7,9 +7,56 @@ local Utils = {} -- @param orig Table to copy -- @param target Table to append to function Utils.shallowCopy(orig, target) - for key, value in pairs(orig) do - target[key] = value - end + for key, value in pairs(orig) do + target[key] = value + end +end + +--- Requires files and puts them in a table. +-- Accepts a table of paths to Lua files: {"path/to/file_1", "path/to/another/file_2", "etc"} +-- Accepts a path to a directory with Lua files: "my_files/here" +-- @param pathOrFiles The table of paths or a path to a directory. +-- @param namespace A table that will hold the required files +-- @treturn table The namespace table +function Utils.loadNamespace(pathOrFiles, namespace) + if (type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table") then + error("bad argument #1 to 'loadNamespace' (string/table of strings expected, got "..type(pathOrFiles)..")", 2) + end + + if (type(pathOrFiles) == "string") then + local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore + if (info == nil or info.type ~= "directory") then + error("bad argument #1 to 'loadNamespace' (path '"..pathOrFiles.."' not found)", 2) + end + + local files = love.filesystem.getDirectoryItems(pathOrFiles) + + for _, file in ipairs(files) do + local name = file:sub(1, #file - 4) + local path = pathOrFiles.."."..name + + local value = require(path) + if namespace then namespace[name] = value end + end + elseif (type(pathOrFiles == "table")) then + for _, path in ipairs(pathOrFiles) do + if (type(path) ~= "string") then + error("bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing "..type(path)..")", 2) -- luacheck: ignore + end + + local name = path + + local dotIndex, slashIndex = path:match("^.*()%."), path:match("^.*()%/") + if (dotIndex or slashIndex) then + name = path:sub((dotIndex or slashIndex) + 1) + end + + local value = require(path) + if namespace then namespace[name] = value end + end + end + + return namespace end return Utils \ No newline at end of file From 9ae805aa43f0f60f98f5653a1fc62193040dc2c4 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 14:56:25 -0300 Subject: [PATCH 058/113] Fixed: Pool:evaluate should bypass Pool:add filter check --- concord/components.lua | 36 ++++++++++++++++++------------------ concord/pool.lua | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/concord/components.lua b/concord/components.lua index e4116fd..d4d6482 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -8,23 +8,23 @@ local Type = require(PATH..".type") local Components = {} local try = function (name) - if type(name) ~= "string" then - return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" - end + if type(name) ~= "string" then + return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" + end - local value = rawget(Components, name) - if not value then - return false, "ComponentClass '"..name.."' does not exist / was not registered" - end + local value = rawget(Components, name) + if not value then + return false, "ComponentClass '"..name.."' does not exist / was not registered" + end - return true, value + 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 rawget(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 @@ -33,25 +33,25 @@ end -- @treturn boolean -- @treturn Component or error string function Components.try(name) - return 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) - local ok, value = try(name) + local ok, value = try(name) - if not ok then error(value, 2) end + if not ok then error(value, 2) end - return value + return value end return setmetatable(Components, { - __index = function(_, name) - local ok, value = try(name) + __index = function(_, name) + local ok, value = try(name) - if not ok then error(value, 2) end - - return value end + if not ok then error(value, 2) end + + return value end }) diff --git a/concord/pool.lua b/concord/pool.lua index e74227a..f1c80eb 100644 --- a/concord/pool.lua +++ b/concord/pool.lua @@ -72,7 +72,7 @@ function Pool:evaluate(e) local eligible = self:eligible(e) if not has and eligible then - self:add(e) + self:add(e, true) --Bypass the check cause we already checked elseif has and not eligible then self:remove(e) end From 079c1d0e18ed52056fc23d1a4b8be3d25c7e601e Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 18:45:15 -0300 Subject: [PATCH 059/113] Utils.shallowCopy as a default serializer/deserializer for Components --- concord/component.lua | 3 +++ concord/utils.lua | 2 ++ 2 files changed, 5 insertions(+) diff --git a/concord/component.lua b/concord/component.lua index 0ac83e6..04691a6 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -4,6 +4,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') local Components = require(PATH..".components") +local Utils = require(PATH..".utils") local Component = {} Component.__mt = { @@ -47,9 +48,11 @@ function Component:__populate() -- luacheck: ignore end function Component:serialize() -- luacheck: ignore + return Utils.shallowCopy(self, {}) end function Component:deserialize(data) -- luacheck: ignore + Utils.shallowCopy(data, self) end -- Internal: Creates a new Component. diff --git a/concord/utils.lua b/concord/utils.lua index 68ae22b..da83c4d 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -10,6 +10,8 @@ function Utils.shallowCopy(orig, target) for key, value in pairs(orig) do target[key] = value end + + return target end --- Requires files and puts them in a table. From d8621e40701448680bda3b67a0091d67c5256c8d Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 14 Mar 2020 19:09:23 -0300 Subject: [PATCH 060/113] Fix serialization function to guard internal values --- concord/component.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/concord/component.lua b/concord/component.lua index 04691a6..d8678f0 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -47,11 +47,18 @@ end function Component:__populate() -- luacheck: ignore end -function Component:serialize() -- luacheck: ignore - return Utils.shallowCopy(self, {}) +function Component:serialize() + local data = Utils.shallowCopy(self, {}) + + --This values shouldn't be copied over + data.__componentClass = nil + data.__isComponent = nil + data.__isComponentClass = nil + + return data end -function Component:deserialize(data) -- luacheck: ignore +function Component:deserialize(data) Utils.shallowCopy(data, self) end From 78dc7ee937e01a595859c075f4dad2e7f02d715c Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sun, 15 Mar 2020 00:20:06 -0300 Subject: [PATCH 061/113] Fixed some error messages --- concord/components.lua | 28 ++++++++++------------- concord/entity.lua | 2 +- concord/system.lua | 6 ++--- concord/world.lua | 51 ++++++++++++++++++++++++++---------------- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/concord/components.lua b/concord/components.lua index d4d6482..bf20d2d 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -7,19 +7,6 @@ local Type = require(PATH..".type") local Components = {} -local try = function (name) - if type(name) ~= "string" then - return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" - end - - local value = rawget(Components, name) - if not value then - return false, "ComponentClass '"..name.."' does not exist / was not registered" - end - - 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 @@ -33,14 +20,23 @@ end -- @treturn boolean -- @treturn Component or error string function Components.try(name) - return try(name) + if type(name) ~= "string" then + return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" + end + + local value = rawget(Components, name) + if not value then + return false, "ComponentClass '"..name.."' does not exist / was not registered" + end + + return true, value end --- Returns the ComponentClass with the specified name -- @string name Name of the ComponentClass to get -- @treturn Component function Components.get(name) - local ok, value = try(name) + local ok, value = Components.try(name) if not ok then error(value, 2) end @@ -49,7 +45,7 @@ end return setmetatable(Components, { __index = function(_, name) - local ok, value = try(name) + local ok, value = Components.try(name) if not ok then error(value, 2) end diff --git a/concord/entity.lua b/concord/entity.lua index acafe01..888353f 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -204,7 +204,7 @@ function Entity:deserialize(data) local componentData = data[i] if (not Components.has(componentData.__name)) then - error("bad argument #1 to 'Entity:deserialize' (ComponentClass "..type(componentData.__name).." wasn't yet loaded)") -- luacheck: ignore + error("bad argument #1 to 'Entity:deserialize' (ComponentClass '"..tostring(componentData.__name).."' wasn't yet loaded)") -- luacheck: ignore end local componentClass = Components[componentData.__name] diff --git a/concord/system.lua b/concord/system.lua index 3583010..756b124 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -51,11 +51,11 @@ local validateFilters = function (baseFilters) for name, componentsList in pairs(baseFilters) do if type(name) ~= 'string' then - error("Invalid name for filter (string key expected, got "..type(name)..")", 3) + 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) + error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3) end local filter = {} @@ -63,7 +63,7 @@ local validateFilters = function (baseFilters) local ok, componentClass = Components.try(component) if not ok then - error("Invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3) + error("invalid component for filter '"..name.."' at position #"..n.." ("..componentClass..")", 3) end filter[#filter + 1] = componentClass diff --git a/concord/world.lua b/concord/world.lua index f42a608..e874881 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -158,37 +158,31 @@ local blacklistedSystemFunctions = { "onDisabled", } ---- Adds a System to the World. --- Callbacks are registered automatically --- Entities added before are added to the System retroactively --- @see World:emit --- @tparam System systemClass SystemClass of System to add --- @treturn World self -function World:addSystem(systemClass) +local tryAddSystem = function (world, systemClass) if (not Type.isSystemClass(systemClass)) then - error("bad argument #1 to 'World:addSystems' (SystemClass expected, got "..type(systemClass)..")", 2) + return false, "SystemClass expected, got "..type(systemClass) end - if (self.__systemLookup[systemClass]) then - error("bad argument #1 to 'World:addSystems' (SystemClass was already added to World)", 2) + if (world.__systemLookup[systemClass]) then + return false, "SystemClass was already added to World" end -- Create instance of system - local system = systemClass(self) + local system = systemClass(world) - self.__systemLookup[systemClass] = system - self.__systems:add(system) + world.__systemLookup[systemClass] = system + world.__systems:add(system) for callbackName, callback in pairs(systemClass) do -- Skip callback if its blacklisted if (not blacklistedSystemFunctions[callbackName]) then -- Make container for all listeners of the callback if it does not exist yet - if (not self.__events[callbackName]) then - self.__events[callbackName] = {} + if (not world.__events[callbackName]) then + world.__events[callbackName] = {} end -- Add callback to listeners - local listeners = self.__events[callbackName] + local listeners = world.__events[callbackName] listeners[#listeners + 1] = { system = system, callback = callback, @@ -197,8 +191,24 @@ function World:addSystem(systemClass) end -- Evaluate all existing entities - for j = 1, self.__entities.size do - system:__evaluate(self.__entities[j]) + for j = 1, world.__entities.size do + system:__evaluate(world.__entities[j]) + end + + return true +end + +--- Adds a System to the World. +-- Callbacks are registered automatically +-- Entities added before are added to the System retroactively +-- @see World:emit +-- @tparam System systemClass SystemClass of System to add +-- @treturn World self +function World:addSystem(systemClass) + local ok, err = tryAddSystem(self, systemClass) + + if not ok then + error("bad argument #1 to 'World:addSystem' ("..err..")", 2) end return self @@ -214,7 +224,10 @@ function World:addSystems(...) for i = 1, select("#", ...) do local systemClass = select(i, ...) - self:addSystem(systemClass) + local ok, err = tryAddSystem(self, systemClass) + if not ok then + error("bad argument #"..i.." to 'World:addSystems' ("..err..")", 2) + end end return self From f64025885211e034ddd24edf6166a3b021dc2b07 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sun, 15 Mar 2020 01:04:39 -0300 Subject: [PATCH 062/113] Fix component:serialize can return nil --- concord/entity.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/concord/entity.lua b/concord/entity.lua index 888353f..854fa8c 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -190,9 +190,11 @@ function Entity:serialize() for _, component in pairs(self.__components) do if component.__name then local componentData = component:serialize() - componentData.__name = component.__name - data[#data + 1] = componentData + if componentData ~= nil then + componentData.__name = component.__name + data[#data + 1] = componentData + end end end From b906e2a910c460fa6c807ea990aa33c4dff15cef Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 16 Mar 2020 10:35:55 +0100 Subject: [PATCH 063/113] Update docs --- README.md | 202 ++++++++++++++++++------------------------------------ 1 file changed, 68 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index d7aea42..d0a2a63 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,16 @@ Auto generated docs for Concord can be found in `docs` folder, or on the [Github - [Entities](#entities) - [Systems](#systems) - [Worlds](#worlds) -- [Assemblages](#assemblages) +- [Assemblages](#assemblages) [Quick Example](#quick-example) [Contributors](#contributors) [License](#licence) - --- ## Installation -Download the repository and copy the 'src' folder in your project. Rename it to something that makes sense (Probably 'concord'), then require it in your project like so: +Download the repository and copy the 'concord' folder into your project. Then require it in your project like so: ```lua local Concord = require("path.to.concord") ``` @@ -42,13 +41,9 @@ local Entity = Concord.entity local Component = Concord.component local System = Concord.system local World = Concord.world -local Assemblage = Concord.assemblage -- Containers local Components = Concord.components -local Systems = Concord.systems -local Worlds = Concord.worlds -local Assemblages = Concord.assemblages ``` --- @@ -71,7 +66,7 @@ What is most important is that Components are data and nothing more. They have 0 Entities are the actual objects in your game. Like a player, an enemy, a crate, or a bullet. Every Entity has it's own set of Components, with their own values. -A crate might have the following components: +A crate might have the following components (Note: Not actual Concord syntax): ```lua { position = { x = 100, y = 200 }, @@ -100,7 +95,7 @@ For this they will only act on Entities that have the Components needed for this In Concord this is done something alike this: ```lua -drawSystem = System({position, texture}) -- Define a System that takes all Entities with a position and texture Component +drawSystem = System({pool = {position, texture}}) -- Define a System that takes all Entities with a position and texture Component function drawSystem:draw() -- Give it a draw function for _, entity in ipairs(self.pool) do -- Iterate over all Entities that this System acts on @@ -140,40 +135,26 @@ When you define a Component or System you are actually defining a `ComponentClas For example. If you want to get a specific Component from an Entity, you'd do `Component = Entity:get(ComponentClass)`. When ComponentClasses or SystemClasses are required it will be written clearly in the Documentation. -#### Containers -Since you'll be defining or creating lots of Components, Systems, Worlds and Assemblages, Concord adds container tables for each of them so that they are easily accessible. +#### Requiring files + +Since you'll have lots of Components and Systems in your game Concord makes it a bit easier to load things in. -These containers can be accessed through ```lua -local Components = require("path.to.concord").components -local Systems = require("path.to.concord").systems -local Worlds = require("path.to.concord").worlds -local Assemblages = require("path.to.concord").aorlds +-- Loads all files in the directory, and puts the return value in the table Systems. The key is their filename without any extension +local Systems = {} +Concord.loadNamespace("path.to.systems", Systems) + +print(Systems.systemName) + +-- Loads all files in the directory. Components automatically register to Concord, so loading them into a namespace isn't necessary. +Concord.loadNamespace("path.to.components") + +print(Systems.componentName) ``` -Concord has helper functions to fill these containers. There are the following options depending on your needs / preference: -```lua --- Loads each file. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' ) -Concord.loadComponents({"path.to.component_1", "path.to.component_2", "etc"}) - --- Loads all files in the directory. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' ) -Concord.loadComponents("path.to.directory.containing.components") - - --- Put the ComponentClass into the container directly. Useful if you want more manual control. Note that you need to require the file in this case -Components.register("componentName", ComponentClass) -``` -Things can then be accessed through their names: -```lua -local component_1_class = Components.component_1 -local componentName_class = Components.componentName -``` - -All the above applies the same to all the other containers. - #### Method chaining ```lua --- All functions that do something ( eg. Don't return anything ) will return self +-- Most (if not all) methods will return self -- This allowes you to chain methods entity @@ -182,8 +163,6 @@ entity :remove(position) :destroy() --- - world :addEntity(fooEntity) :addEntity(barEntity) @@ -192,13 +171,13 @@ world ``` ### Components -When defining a ComponentClass you usually pass in a `populate` function. This will fill the Component with values. +When defining a ComponentClass you need to pass in a name and usually a `populate` function. This will fill the Component with values. ```lua --- Create the ComponentClass with a populate function +-- Create the position class with a populate function -- The component variable is the actual Component given to an Entity -- The x and y variables are values we pass in when we create the Component -local positionComponentClass = Concord.component(function(component, x, y) +Concord.component("position" function(component, x, y) component.x = x or 0 component.y = y or 0 end) @@ -206,19 +185,11 @@ end) -- Create a ComponentClass without a populate function -- Components of this type won't have any fields. -- This can be useful to indiciate state. -local pushableComponentClass = Concord.component() -``` - -```lua --- Manually register the ComponentClass to the container if we want -Concord.components.register("positionComponent", positionComponentClass) - --- Otherwise return the ComponentClass so it can be required -return positionComponentClass +local pushableComponentClass = Concord.component("position") ``` ### Entities -Entities can be freely made and be given Components. You pass the ComponentClass and the values you want to pass. It will then create the Component for you. +Entities can be freely made and be given Components. You pass the name of the ComponentClass and the values you want to pass. It will then create the Component for you. Entities can only have a maximum of one of each Component. Entities can not share Components. @@ -233,43 +204,41 @@ local myEntity = Entity(myWorld) -- To add it to a world immediately ( See World ```lua -- Give the entity the position Component defined above -- x will become 100. y will become 50 -myEntity:give(positionComponentClass, 100, 50) +myEntity:give("position", 100, 50) ``` ```lua -- Retrieve a Component -local positionComponent = myEntity[positionComponentClass] --- or -local positionComponent = myEntity:get(positionComponentClass) +local position = myEntity.position -print(positionComponent.x, positionComponent.y) -- 100, 50 +print(position.x, position.y) -- 100, 50 ``` ```lua -- Remove a Component -myEntity:remove(positionComponentClass) +myEntity:remove("position") ``` ```lua -- Check if the Entity has a Component -local hasPositionComponent = myEntity:has(positionComponentClass) -print(hasPositionComponent) -- false +local hasPosition = myEntity.position and true of false +print(hasPosition) -- false ``` ```lua -- Entity:give will override a Component if the Entity already has it -- Entity:ensure will only put the Component if the Entity does not already have it -Entity:ensure(positionComponentClass, 0, 0) -- Will give +Entity:ensure("position", 0, 0) -- Will give -- Position is {x = 0, y = 0} -Entity:give(positionComponentClass, 50, 50) -- Will override +Entity:give("position", 50, 50) -- Will override -- Position is {x = 50, y = 50} -Entity:give(positionComponentClass, 100, 100) -- Will override +Entity:give("position", 100, 100) -- Will override -- Position is {x = 100, y = 100} -Entity:ensure(positionComponentClass, 0, 0) -- Wont do anything +Entity:ensure("position", 0, 0) -- Wont do anything -- Position is {x = 100, y = 100} ``` @@ -285,7 +254,7 @@ end ```lua -- Assemble the Entity ( See Assemblages ) -myEntity:assemble(myAssemblage, 100, true, "foo") +myEntity:assemble(assemblageFunction, 100, true, "foo") ``` ```lua @@ -310,30 +279,21 @@ Systems can have multiple pools. ```lua -- Create a System -local mySystemClass = Concord.system({positionComponentClass}) -- Pool will contain all Entities with a position Component +local mySystemClass = Concord.system(pool = {"position"}) -- Pool named 'pool' will contain all Entities with a position Component -- Create a System with multiple pools local mySystemClass = Concord.system( - { -- This Pool's name will default to 'pool' - positionCompomponentClass, - velocityComponentClass, + pool = { -- This pool will be named 'pool' + "position", + "velocity", }, - { -- This Pool's name will be 'secondPool' - healthComponentClass, - damageableComponentClass, - "secondPool", + secondPool = { -- This pool's name will be 'secondPool' + "health", + "damageable", } ) ``` -```lua --- Manually register the SystemClass to the container if we want -Concord.system.register("mySystem", mySystemClass) - --- Otherwise return the SystemClass so it can be required -return mySystemClass -``` - ```lua -- If a System has a :init function it will be called on creation @@ -348,16 +308,11 @@ end function mySystemClass:update(dt) -- Iterate over all entities in the Pool for _, e in ipairs(self.pool) - -- Get the Entity's Components - local positionComponent = e[positionComponentClass] - local velocityComponent = e[velocityComponentClass] - -- Do something with the Components - positionComponent.x = positionComponent.x + velocityComponent.x * dt - positionComponent.y = positionComponent.y + velocityComponent.y * dt + e.position.x = e.position.x + e.velocity.x * dt + e.position.y = e.position.y + e.velocity.y * dt end - -- Iterate over all entities in the second Pool for _, e in ipairs(self.secondPool) -- Do something @@ -367,24 +322,20 @@ end ```lua -- Systems can be enabled and disabled +-- When systems are disabled their callbacks won't be executed. +-- Note that pools will still be updated +-- This is mainly useful for systems that display debug information -- Systems are enabled by default -- Enable a System -mySystem:enable() --- or mySystem:setEnable(true) -- Disable a System -mySystem:disable() --- or mySystem:setEnable(false) --- Toggle the enable state -mySystem:toggleEnabled() - -- Get enabled state local isEnabled = mySystem:isEnabled() -print(isEnabled) -- true +print(isEnabled) -- false ``` ```lua @@ -405,14 +356,6 @@ Worlds can have any number of Entities. local myWorld = Concord.world() ``` -```lua --- Manually register the World to the container if we want -Concord.worlds.register("myWorld", myWorld) - --- Otherwise return the World so it can be required -return myWorld -``` - ```lua -- Add an Entity to the World myWorld:addEntity(myEntity) @@ -471,33 +414,31 @@ end ### Assemblages -Assemblages are helpers to 'make' Entities something. +Assemblages are functions to 'make' Entities something. An important distinction is that they _append_ Components. ```lua --- Make an Assemblage +-- Make an Assemblage function -- e is the Entity being assembled. -- cuteness and legs are variables passed in -local animalAssemblage(function(e, cuteness, legs) +function animal(e, cuteness, legs) e :give(cutenessComponentClass, cuteness) :give(limbs, legs, 0) -- Variable amount of legs. 0 arm. end) --- Make an Assemblage that used animalAssemblage +-- Make an Assemblage that uses animal -- cuteness is a variables passed in -local catAssemblage(function(e, cuteness) +function cat(e, cuteness) e - :assemble(animalAssemblage, cuteness * 2, 4) -- Cats are twice as cute, and have 4 legs. + :assemble(animal, cuteness * 2, 4) -- Cats are twice as cute, and have 4 legs. :give(soundComponent, "meow.mp3") end) ``` ```lua -- Use an Assemblage -myEntity:assemble(catAssemblage, 100) -- 100 cuteness --- or -catAssemblage:assemble(myEntity, 100) -- 100 cuteness +myEntity:assemble(cat, 100) -- 100 cuteness ``` --- @@ -506,14 +447,13 @@ catAssemblage:assemble(myEntity, 100) -- 100 cuteness ```lua local Concord = require("concord") --- Defining ComponentClasses --- I use UpperCamelCase to indicate its a class -local Position = Concord.component(function(c, x, y) +-- Defining components +Concord.component("position", function(c, x, y) c.x = x or 0 c.y = y or 0 end) -local Velocity = Concord.component(function(c, x, y) +Concord.component("velocity", function(c, x, y) c.x = x or 0 c.y = y or 0 end) @@ -522,27 +462,21 @@ local Drawable = Concord.component() -- Defining Systems -local MoveSystem = Concord.system({Position, Velocity}) +local MoveSystem = Concord.system(pool = {"position", "velocity"}) function MoveSystem:update(dt) for _, e in ipairs(self.pool) do - -- I use lowerCamelCase to indicate its an instance - local position = e[Position] - local velocity = e[Velocity] - - position.x = position.x + velocity.x * dt - position.y = position.y + velocity.y * dt + e.position.x = e.position.x + e.velocity.x * dt + e.position.y = e.position.y + e.velocity.y * dt end end -local DrawSystem = Concord.system({Position, Drawable}) +local DrawSystem = Concord.system(pool = {"position", "drawable"}) function DrawSystem:draw() for _, e in ipairs(self.pool) do - local position = e[Position] - - love.graphics.circle("fill", position.x, position.y, 5) + love.graphics.circle("fill", e.position.x, e.position.y, 5) end end @@ -555,18 +489,18 @@ world:addSystems(MoveSystem, DrawSystem) -- This Entity will be rendered on the screen, and move to the right at 100 pixels a second local entity_1 = Concord.entity(world) -:give(Position, 100, 100) -:give(Velocity, 100, 0) -:give(Drawable) +:give("position", 100, 100) +:give("velocity", 100, 0) +:give("drawable") -- This Entity will be rendered on the screen, and stay at 50, 50 local entity_2 = Concord.entity(world) -:give(Position, 50, 50) -:give(Drawable) +:give("position", 50, 50) +:give("drawable") -- This Entity does exist in the World, but since it doesn't match any System's filters it won't do anything local entity_3 = Concord.entity(world) -:give(Position, 200, 200) +:give("position", 200, 200) -- Emit the events From c889b15d446999483bfaa6d3ddcdc6c506b924f6 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 2 May 2020 20:42:26 +0200 Subject: [PATCH 064/113] Remove old examples --- examples/assemblageTest/init.lua | 63 ---------- examples/baseLayout/main.lua | 6 - examples/baseLayout/src/components/init.lua | 5 - examples/baseLayout/src/systems/init.lua | 5 - examples/serialization/init.lua | 67 ----------- examples/simpleDrawing/init.lua | 125 -------------------- main.lua | 1 - 7 files changed, 272 deletions(-) delete mode 100644 examples/assemblageTest/init.lua delete mode 100644 examples/baseLayout/main.lua delete mode 100644 examples/baseLayout/src/components/init.lua delete mode 100644 examples/baseLayout/src/systems/init.lua delete mode 100644 examples/serialization/init.lua delete mode 100644 examples/simpleDrawing/init.lua delete mode 100644 main.lua diff --git a/examples/assemblageTest/init.lua b/examples/assemblageTest/init.lua deleted file mode 100644 index 4136936..0000000 --- a/examples/assemblageTest/init.lua +++ /dev/null @@ -1,63 +0,0 @@ -local Concord = require("src") - -local Entity = Concord.entity -local Component = Concord.component -local System = Concord.system -local Assemblage = Concord.assemblage - -local Legs = Component("Legs", function(e, legCount) - e.legCount = legCount or 0 -end) - -local Instinct = Component("Instinct", function(e) -- luacheck: ignore -end) - -local Cool = Component("Cool", function(e, coolness) - e.coolness = coolness -end) - -local Wings = Component("Wings", function(e) - e.wingCount = 2 -end) - - -local Animal = function(e, legCount) - e - :give("Legs", legCount) - :give("Instinct") - - print("Animal") -end - -local Lion = function(e, coolness) - e - :assemble(Animal, 4) - :give(Cool, coolness) - - print("Lion") -end - -local Eagle = function(e) - e - :assemble(Animal, 2) - :give(Wings) - - print("Eagle") -end - -local Griffin = function(e, coolness) - e - :assemble(Animal, 4) - :assemble(Lion, coolness * 2) - :assemble(Eagle) -end - - -local myAnimal = Entity() -:assemble(Griffin, 5) ---:apply() - -print(myAnimal:has("Legs")) -print(myAnimal:has("Instinct")) -print(myAnimal:has("Cool")) -print(myAnimal:has("Wings")) diff --git a/examples/baseLayout/main.lua b/examples/baseLayout/main.lua deleted file mode 100644 index 6d6717f..0000000 --- a/examples/baseLayout/main.lua +++ /dev/null @@ -1,6 +0,0 @@ -local PATH = (...):gsub('%.[^%.]+$', '') - -local Concord = require("src") - -local C = require(PATH..".src.components") -local S = require(PATH..".src.systems") diff --git a/examples/baseLayout/src/components/init.lua b/examples/baseLayout/src/components/init.lua deleted file mode 100644 index 60a5a4c..0000000 --- a/examples/baseLayout/src/components/init.lua +++ /dev/null @@ -1,5 +0,0 @@ -local PATH = (...):gsub('%.init$', '') - -return { - -} \ No newline at end of file diff --git a/examples/baseLayout/src/systems/init.lua b/examples/baseLayout/src/systems/init.lua deleted file mode 100644 index 0273ef6..0000000 --- a/examples/baseLayout/src/systems/init.lua +++ /dev/null @@ -1,5 +0,0 @@ -local PATH = (...):gsub('%.init$', '') - -return { - -} \ No newline at end of file diff --git a/examples/serialization/init.lua b/examples/serialization/init.lua deleted file mode 100644 index c362265..0000000 --- a/examples/serialization/init.lua +++ /dev/null @@ -1,67 +0,0 @@ -local Concord = require("concord") - -local function display(t) - print("Table: " ..tostring(t)) - for key, value in pairs(t) do - if type(value) == "table" then - display(value) - else - print(key, value) - end - end -end - -local test_component_1 = Concord.component("test_component_1", function(e, x, y) - e.x = x or 0 - e.y = y or 0 -end) - -function test_component_1:serialize() - return { - x = self.x, - y = self.y, - } -end - -function test_component_1:deserialize(data) - self.x = data.x or 0 - self.y = data.y or 0 -end - -local test_component_2 = Concord.component("test_component_2", function(e, foo) - e.foo = foo -end) - -function test_component_2:serialize() - return { - foo = self.foo - } -end - -function test_component_2:deserialize(data) - self.foo = data.foo -end - --- Test worlds -local world_1 = Concord.world() -local world_2 = Concord.world() - --- Test Entity -Concord.entity(world_1) -:give("test_component_1", 100, 50) -:give("test_component_2", "Hello World!") - --- Serialize world -local data = world_1:serialize() - --- Deserialize world -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"] - -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 deleted file mode 100644 index 6afd986..0000000 --- a/examples/simpleDrawing/init.lua +++ /dev/null @@ -1,125 +0,0 @@ -local Concord = require("src") - -local Entity = Concord.entity -local Component = Concord.component -local System = Concord.system - -local Game = Concord.world() - -local Position = Component('Position', function(e, x, y) - e.x = x - e.y = y -end) - -local Rectangle = Component('Rectangle', function(e, w, h) - e.w = w - e.h = h -end) - -local Circle = Component('Circle', function(e, r) - e.r = r -end) - -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{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') - - love.graphics.setColor(255, 255, 255) - if color then - love.graphics.setColor(color.r, color.g, color.b, color.a) - end - - love.graphics.rectangle("fill", position.x, position.y, rectangle.w, rectangle.h) - end -end - -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:") - end -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') - - love.graphics.setColor(255, 255, 255) - if color then - love.graphics.setColor(color.r, color.g, color.b, color.a) - end - - love.graphics.circle("fill", position.x, position.y, circle.r) - end -end - -local RandomRemover = System{pool = {}} - -function RandomRemover:init() - self.time = 0 -end - -function RandomRemover:update(dt) - self.time = self.time + dt - - if self.time >= 0.05 then - self.time = 0 - - if self.pool.size > 0 then - local i = love.math.random(1, self.pool.size) - - self.pool:get(i):destroy() - end - end - - love.window.setTitle(love.timer.getFPS()) -end - -Game:addSystem(RandomRemover(), "update") -Game:addSystem(RectangleRenderer(), "draw") -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)) - - if love.math.random(0, 1) == 0 then - e:give('Color', love.math.random(), love.math.random(), love.math.random(), 1) - end - - Game:addEntity(e) -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)) - - if love.math.random(0, 1) == 0 then - e:give('Color', love.math.random(), love.math.random(), love.math.random(), 1) - end - - Game:addEntity(e) -end - - -function love.update(dt) - Game:emit("update", dt) -end - -function love.draw() - Game:emit("draw") -end diff --git a/main.lua b/main.lua deleted file mode 100644 index 7cfd88a..0000000 --- a/main.lua +++ /dev/null @@ -1 +0,0 @@ -require("examples.serialization") \ No newline at end of file From d56821205def03e7a149fecadfe06ffb3c7c67a0 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 2 May 2020 20:44:33 +0200 Subject: [PATCH 065/113] Make repo submodule friendly --- init.lua | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 init.lua diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..2a73dea --- /dev/null +++ b/init.lua @@ -0,0 +1,3 @@ +local PATH = (...):gsub('%.init$', '') + +return require(PATH..".concord") \ No newline at end of file From c95a1f27685b503f31f87a6042a5947e03db7273 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 2 May 2020 20:44:58 +0200 Subject: [PATCH 066/113] Update license --- concord/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concord/init.lua b/concord/init.lua index cf5f2f2..705289a 100644 --- a/concord/init.lua +++ b/concord/init.lua @@ -9,7 +9,7 @@ local Concord = { _LICENCE = [[ MIT LICENSE - Copyright (c) 2018 Justin van der Leij / Tjakka5 + Copyright (c) 2020 Justin van der Leij / Tjakka5 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 3e9c596187d35c850b06bb71ad7045794fd16588 Mon Sep 17 00:00:00 2001 From: Tjakka5 Date: Sat, 2 May 2020 20:51:15 +0200 Subject: [PATCH 067/113] Fix mistakes in readme --- README.md | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d0a2a63..fb549c5 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,8 @@ drawSystem = System({pool = {position, texture}}) -- Define a System that takes function drawSystem:draw() -- Give it a draw function for _, entity in ipairs(self.pool) do -- Iterate over all Entities that this System acts on - local position = entity[position] -- Get the position Component of this Entity - local texture = entity[texture] -- Get the texture Component of this Entity + local position = entity.position -- Get the position Component of this Entity + local texture = entity.texture -- Get the texture Component of this Entity -- Draw the Entity love.graphics.draw(texture.image, position.x, position.y) @@ -128,13 +128,6 @@ And all that without writing a single extra line of code. Just reusing code that Concord does a few things that might not be immediately clear. This segment should help understanding. -#### Classes - -When you define a Component or System you are actually defining a `ComponentClass` and `SystemClass` respectively. From these instances of them can be created. They also act as identifiers for Concord. - -For example. If you want to get a specific Component from an Entity, you'd do `Component = Entity:get(ComponentClass)`. -When ComponentClasses or SystemClasses are required it will be written clearly in the Documentation. - #### Requiring files Since you'll have lots of Components and Systems in your game Concord makes it a bit easier to load things in. @@ -142,14 +135,14 @@ Since you'll have lots of Components and Systems in your game Concord makes it a ```lua -- Loads all files in the directory, and puts the return value in the table Systems. The key is their filename without any extension local Systems = {} -Concord.loadNamespace("path.to.systems", Systems) +Concord.utils.loadNamespace("path/to/systems", Systems) print(Systems.systemName) --- Loads all files in the directory. Components automatically register to Concord, so loading them into a namespace isn't necessary. -Concord.loadNamespace("path.to.components") +-- Loads all files in the directory. Components automatically register into Concord.components, so loading them into a namespace isn't necessary. +Concord.utils.loadNamespace("path/to/components") -print(Systems.componentName) +print(Concord.components.componentName) ``` #### Method chaining @@ -157,13 +150,13 @@ print(Systems.componentName) -- Most (if not all) methods will return self -- This allowes you to chain methods -entity -:give(position, 100, 50) -:give(velocity, 200, 0) -:remove(position) +myEntity +:give("position", 100, 50) +:give("velocity", 200, 0) +:remove("position") :destroy() -world +myWorld :addEntity(fooEntity) :addEntity(barEntity) :clear() @@ -219,12 +212,6 @@ print(position.x, position.y) -- 100, 50 myEntity:remove("position") ``` -```lua --- Check if the Entity has a Component -local hasPosition = myEntity.position and true of false -print(hasPosition) -- false -``` - ```lua -- Entity:give will override a Component if the Entity already has it -- Entity:ensure will only put the Component if the Entity does not already have it @@ -462,7 +449,9 @@ local Drawable = Concord.component() -- Defining Systems -local MoveSystem = Concord.system(pool = {"position", "velocity"}) +local MoveSystem = Concord.system({ + pool = {"position", "velocity"} +}) function MoveSystem:update(dt) for _, e in ipairs(self.pool) do @@ -472,7 +461,9 @@ function MoveSystem:update(dt) end -local DrawSystem = Concord.system(pool = {"position", "drawable"}) +local DrawSystem = Concord.system({ + pool = {"position", "drawable"} +}) function DrawSystem:draw() for _, e in ipairs(self.pool) do @@ -525,5 +516,5 @@ end --- -## Licence +## License MIT Licensed - Copyright Justin van der Leij (Tjakka5) From 7979226c3314de5dded9d126c6fb962f4b1ff721 Mon Sep 17 00:00:00 2001 From: Josh Perry Date: Tue, 18 Aug 2020 15:20:56 +0100 Subject: [PATCH 068/113] Regenerated docs --- docs/classes/Component.html | 6 +- docs/classes/Entity.html | 15 +-- docs/classes/List.html | 186 ++++++++++++++++------------------- docs/classes/Pool.html | 106 +++++++++++--------- docs/classes/System.html | 89 ++--------------- docs/classes/World.html | 9 +- docs/index.html | 23 +---- docs/modules/Components.html | 185 ++++++++++++++++++++++++++++++++++ docs/modules/Concord.html | 121 +---------------------- docs/modules/type.html | 36 +------ docs/modules/utils.html | 41 +++++++- 11 files changed, 387 insertions(+), 430 deletions(-) create mode 100644 docs/modules/Components.html diff --git a/docs/classes/Component.html b/docs/classes/Component.html index ad4f3e2..7e0358c 100644 --- a/docs/classes/Component.html +++ b/docs/classes/Component.html @@ -38,7 +38,6 @@

    Classes

    Modules

    @@ -163,7 +159,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/classes/Entity.html b/docs/classes/Entity.html index 5ecd569..b263a9f 100644 --- a/docs/classes/Entity.html +++ b/docs/classes/Entity.html @@ -38,7 +38,6 @@

    Classes

    Modules

    @@ -64,8 +60,7 @@

    Class Entity

    An object that exists in a world.

    An entity - contains components which are processed by systems. -

    + contains components which are processed by systems.

    Methods

    @@ -251,11 +246,11 @@

    Parameters:

    • assemblage - Assemblage - Assemblage to assemble with + function + Function that will assemble an entity
    • ... - additional arguments to pass to the Assemblage's assemble function. + additional arguments to pass to the assemblage function.
    @@ -413,7 +408,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/classes/List.html b/docs/classes/List.html index b6becb6..414888a 100644 --- a/docs/classes/List.html +++ b/docs/classes/List.html @@ -33,13 +33,11 @@

    Contents

    Classes

    Modules

    @@ -74,6 +69,18 @@ Creates a new List. + List:add (obj) + Adds an object to the List. + + + List:remove (obj) + Removes an object from the List. + + + List:clear () + Clears the List completely. + + List:has (obj) Returns true if the List has the object. @@ -86,21 +93,6 @@ Returns the index of an object in the List. -

    Metamethods

    - - - - - - - - - - - - - -
    List:__add (obj)Adds an object to the List.
    List:__remove (obj)Removes an object from the List.
    List:__clear ()Clears the List completely.


    @@ -128,6 +120,80 @@ + +
    + + List:add (obj) +
    +
    + Adds an object to the List. + Object must be of reference type + Object may not be the string 'size' + + +

    Parameters:

    +
      +
    • obj + Object to add +
    • +
    + +

    Returns:

    +
      + + List + self +
    + + + + +
    +
    + + List:remove (obj) +
    +
    + Removes an object from the List. + + +

    Parameters:

    +
      +
    • obj + Object to remove +
    • +
    + +

    Returns:

    +
      + + List + self +
    + + + + +
    +
    + + List:clear () +
    +
    + Clears the List completely. + + + +

    Returns:

    +
      + + List + self +
    + + + +
    @@ -206,84 +272,6 @@ - - -

    Metamethods

    - -
    -
    - - List:__add (obj) -
    -
    - Adds an object to the List. - Object must be of reference type - Object may not be the string 'size' - - -

    Parameters:

    -
      -
    • obj - Object to add -
    • -
    - -

    Returns:

    -
      - - List - self -
    - - - - -
    -
    - - List:__remove (obj) -
    -
    - Removes an object from the List. - - -

    Parameters:

    -
      -
    • obj - Object to remove -
    • -
    - -

    Returns:

    -
      - - List - self -
    - - - - -
    -
    - - List:__clear () -
    -
    - Clears the List completely. - - - -

    Returns:

    -
      - - List - self -
    - - - -
    @@ -292,7 +280,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/classes/Pool.html b/docs/classes/Pool.html index 184ad8c..77be777 100644 --- a/docs/classes/Pool.html +++ b/docs/classes/Pool.html @@ -33,13 +33,11 @@

    Contents

    Classes

    Modules

    @@ -75,6 +70,14 @@ Creates a new Pool + Pool:eligible (e) + Checks if an Entity is eligible for the Pool. + + + Pool:evaluate (e) + Evaluate whether an Entity should be added or removed from the Pool. + + Pool:getName () Gets the name of the Pool @@ -87,13 +90,6 @@ Callback for when an Entity is added to the Pool. -

    Metamethods

    - - - - - -
    Pool:__eligible (e)Checks if an Entity is eligible for the Pool.


    @@ -132,6 +128,59 @@ + +
    + + Pool:eligible (e) +
    +
    + Checks if an Entity is eligible for the Pool. + + +

    Parameters:

    +
      +
    • e + Entity + Entity to check +
    • +
    + +

    Returns:

    +
      + + boolean + +
    + + + + +
    +
    + + Pool:evaluate (e) +
    +
    + Evaluate whether an Entity should be added or removed from the Pool. + + +

    Parameters:

    +
      +
    • e + Entity to add or remove +
    • +
    + +

    Returns:

    +
      + + Pool + self +
    + + + +
    @@ -193,37 +242,6 @@ - - -

    Metamethods

    - -
    -
    - - Pool:__eligible (e) -
    -
    - Checks if an Entity is eligible for the Pool. - - -

    Parameters:

    -
      -
    • e - Entity - Entity to check -
    • -
    - -

    Returns:

    -
      - - boolean - -
    - - - -
    @@ -232,7 +250,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/classes/System.html b/docs/classes/System.html index 914bd9b..a73ed8b 100644 --- a/docs/classes/System.html +++ b/docs/classes/System.html @@ -39,7 +39,6 @@

    Classes

    Modules

    @@ -66,29 +62,16 @@

    Iterates over Entities.

    From these Entities its get Components and modify them. A System contains 1 or more Pools. - A System is contained by 1 World. -

    + A System is contained by 1 World.

    Methods

    - + - - - - - - - - - - - - @@ -134,7 +117,7 @@
    - System:new (...) + System:new (table)
    Creates a new SystemClass. @@ -142,8 +125,8 @@

    Parameters:

      -
    • ... - Variable amounts of filters +
    • table + filters A table containing filters (name = {components...})
    @@ -157,66 +140,6 @@ -
    -
    - - System:enable () -
    -
    - Enables the System. - - - -

    Returns:

    -
      - - System - self -
    - - - - -
    -
    - - System:disable () -
    -
    - Disables the System. - - - -

    Returns:

    -
      - - System - self -
    - - - - -
    -
    - - System:toggleEnabled () -
    -
    - Toggles if the System is enabled. - - - -

    Returns:

    -
      - - System - self -
    - - - -
    @@ -385,7 +308,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/classes/World.html b/docs/classes/World.html index ce29521..e673fa8 100644 --- a/docs/classes/World.html +++ b/docs/classes/World.html @@ -38,7 +38,6 @@

    Classes

    Modules

    @@ -66,8 +62,7 @@

    A world emits to let Systems iterate. A World contains any amount of Systems. - A World contains any amount of Entities. -

    + A World contains any amount of Entities.

    Methods

    @@ -466,7 +461,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/index.html b/docs/index.html index 427d6b7..8950dda 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,17 +31,13 @@

    Modules

    Classes

    System:new (...)System:new (table) Creates a new SystemClass.
    System:enable ()Enables the System.
    System:disable ()Disables the System.
    System:toggleEnabled ()Toggles if the System is enabled.
    System:setEnabled (enable) Sets if the System is enabled
    - - - - @@ -71,10 +63,6 @@ - - - - - - - -
    AssemblagesA container for registered Assemblages
    Components Container for registered ComponentClassesConcord
    SystemsContainer for registered SystemClasses
    type Type @@ -85,18 +73,9 @@ Utils Helper module for misc operations
    worldsWorlds - Container for registered Worlds

    Classes

    - - - - @@ -128,7 +107,7 @@
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/modules/Components.html b/docs/modules/Components.html new file mode 100644 index 0000000..de521c2 --- /dev/null +++ b/docs/modules/Components.html @@ -0,0 +1,185 @@ + + + + + Reference + + + + +
    + +
    + +
    +
    +
    + + +
    + + + + + + +
    + +

    Module Components

    +

    Container for registered ComponentClasses

    +

    + + +

    Functions

    +
    AssemblageGives an entity a set of components.
    Component A pure data container that is contained by a single entity.
    + + + + + + + + + + + + +
    has (name)Returns true if the containter has the ComponentClass with the specified name
    try (name)Returns true and the ComponentClass if one was registered with the specified name + or false and an error otherwise
    get (name)Returns the ComponentClass with the specified name
    + +
    +
    + + +

    Functions

    + +
    +
    + + has (name) +
    +
    + Returns true if the containter has the ComponentClass with the specified name + + +

    Parameters:

    +
      +
    • name + string + Name of the ComponentClass to check +
    • +
    + +

    Returns:

    +
      + + boolean + +
    + + + + +
    +
    + + try (name) +
    +
    + Returns true and the ComponentClass if one was registered with the specified name + or false and an error otherwise + + +

    Parameters:

    +
      +
    • name + string + Name of the ComponentClass to check +
    • +
    + +

    Returns:

    +
      +
    1. + boolean +
    2. +
    3. + Component + or error string
    4. +
    + + + + +
    +
    + + get (name) +
    +
    + Returns the ComponentClass with the specified name + + +

    Parameters:

    +
      +
    • name + string + Name of the ComponentClass to get +
    • +
    + +

    Returns:

    +
      + + Component + +
    + + + + +
    +
    + + + + +
    +generated by LDoc 1.4.6 +Last updated 2020-08-18 15:20:32 +
    + + + diff --git a/docs/modules/Concord.html b/docs/modules/Concord.html index b50ef54..903787f 100644 --- a/docs/modules/Concord.html +++ b/docs/modules/Concord.html @@ -30,25 +30,17 @@
  • Index
-

Contents

-

Modules

Classes

    -
  • Assemblage
  • Component
  • Entity
  • List
  • @@ -66,129 +58,18 @@

    -

    Functions

    - - - - - - - - - - - - - - - - - -
    loadComponents (pathOrFiles)Loads ComponentClasses and puts them in the Components container.
    loadSystems (pathOrFiles)Loads SystemClasses and puts them in the Systems container.
    loadWorlds (pathOrFiles)Loads Worlds and puts them in the Worlds container.
    loadAssemblages (pathOrFiles)Loads Assemblages and puts them in the Assemblages container.


    -

    Functions

    - -
    -
    - - loadComponents (pathOrFiles) -
    -
    - 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" - - -

    Parameters:

    -
      -
    • pathOrFiles - -
    • -
    - - - - - -
    -
    - - loadSystems (pathOrFiles) -
    -
    - 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" - - -

    Parameters:

    -
      -
    • pathOrFiles - -
    • -
    - - - - - -
    -
    - - loadWorlds (pathOrFiles) -
    -
    - 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" - - -

    Parameters:

    -
      -
    • pathOrFiles - -
    • -
    - - - - - -
    -
    - - loadAssemblages (pathOrFiles) -
    -
    - 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" - - -

    Parameters:

    -
      -
    • pathOrFiles - -
    • -
    - - - - - -
    -
    generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
    diff --git a/docs/modules/type.html b/docs/modules/type.html index 923c547..2fb678f 100644 --- a/docs/modules/type.html +++ b/docs/modules/type.html @@ -38,17 +38,13 @@

    Modules

    Classes

      -
    • Assemblage
    • Component
    • Entity
    • List
    • @@ -93,10 +89,6 @@ Type.isWorld (t) Returns if object is a World. - - Type.isAssemblage (t) - Returns if object is an Assemblage. -
      @@ -261,32 +253,6 @@ - -
      - - Type.isAssemblage (t) -
      -
      - Returns if object is an Assemblage. - - -

      Parameters:

      -
        -
      • t - Object to check -
      • -
      - -

      Returns:

      -
        - - boolean - -
      - - - -
      @@ -295,7 +261,7 @@
      generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
      diff --git a/docs/modules/utils.html b/docs/modules/utils.html index 30d8e2a..35c2e4c 100644 --- a/docs/modules/utils.html +++ b/docs/modules/utils.html @@ -38,17 +38,13 @@

      Modules

      Classes

        -
      • Assemblage
      • Component
      • Entity
      • List
      • @@ -73,6 +69,10 @@ Utils.shallowCopy (orig, target) Does a shallow copy of a table and appends it to a target table. + + Utils.loadNamespace (pathOrFiles, namespace) + Requires files and puts them in a table. +
        @@ -104,6 +104,37 @@ + +
        + + Utils.loadNamespace (pathOrFiles, namespace) +
        +
        + Requires files and puts them in a table. + Accepts a table of paths to Lua files: {"path/to/file_1", "path/to/another/file_2", "etc"} + Accepts a path to a directory with Lua files: "my_files/here" + + +

        Parameters:

        +
          +
        • pathOrFiles + The table of paths or a path to a directory. +
        • +
        • namespace + A table that will hold the required files +
        • +
        + +

        Returns:

        +
          + + table + The namespace table +
        + + + +
        @@ -112,7 +143,7 @@
        generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
        From 2196a41e715770ccf9f422477b1d3af5baab16a5 Mon Sep 17 00:00:00 2001 From: Daniel Power Date: Sun, 15 Nov 2020 23:44:15 -0330 Subject: [PATCH 069/113] Fix error in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb549c5..d5b9922 100644 --- a/README.md +++ b/README.md @@ -445,7 +445,7 @@ Concord.component("velocity", function(c, x, y) c.y = y or 0 end) -local Drawable = Concord.component() +local Drawable = Concord.component("drawable") -- Defining Systems From eb82c3c580f0f99d73c34a2191b6dff9c69d585d Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Sat, 9 Jan 2021 19:19:58 -0300 Subject: [PATCH 070/113] Update version This will be version 3.0 of Concord --- concord/init.lua | 2 +- docs/modules/Components.html | 185 ----------------------------------- docs/modules/components.html | 71 +++++++------- 3 files changed, 38 insertions(+), 220 deletions(-) delete mode 100644 docs/modules/Components.html diff --git a/concord/init.lua b/concord/init.lua index 705289a..1c09669 100644 --- a/concord/init.lua +++ b/concord/init.lua @@ -4,7 +4,7 @@ local PATH = (...):gsub('%.init$', '') local Concord = { - _VERSION = "2.0 Beta", + _VERSION = "3.0", _DESCRIPTION = "A feature-complete ECS library", _LICENCE = [[ MIT LICENSE diff --git a/docs/modules/Components.html b/docs/modules/Components.html deleted file mode 100644 index de521c2..0000000 --- a/docs/modules/Components.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Reference - - - - -
        - -
        - -
        -
        -
        - - -
        - - - - - - -
        - -

        Module Components

        -

        Container for registered ComponentClasses

        -

        - - -

        Functions

        - - - - - - - - - - - - - -
        has (name)Returns true if the containter has the ComponentClass with the specified name
        try (name)Returns true and the ComponentClass if one was registered with the specified name - or false and an error otherwise
        get (name)Returns the ComponentClass with the specified name
        - -
        -
        - - -

        Functions

        - -
        -
        - - has (name) -
        -
        - Returns true if the containter has the ComponentClass with the specified name - - -

        Parameters:

        -
          -
        • name - string - Name of the ComponentClass to check -
        • -
        - -

        Returns:

        -
          - - boolean - -
        - - - - -
        -
        - - try (name) -
        -
        - Returns true and the ComponentClass if one was registered with the specified name - or false and an error otherwise - - -

        Parameters:

        -
          -
        • name - string - Name of the ComponentClass to check -
        • -
        - -

        Returns:

        -
          -
        1. - boolean -
        2. -
        3. - Component - or error string
        4. -
        - - - - -
        -
        - - get (name) -
        -
        - Returns the ComponentClass with the specified name - - -

        Parameters:

        -
          -
        • name - string - Name of the ComponentClass to get -
        • -
        - -

        Returns:

        -
          - - Component - -
        - - - - -
        -
        - - -
        -
        -
        -generated by LDoc 1.4.6 -Last updated 2020-08-18 15:20:32 -
        -
        - - diff --git a/docs/modules/components.html b/docs/modules/components.html index f97534d..de521c2 100644 --- a/docs/modules/components.html +++ b/docs/modules/components.html @@ -38,17 +38,13 @@

        Modules

        Classes

          -
        • Assemblage
        • Component
        • Entity
        • List
        • @@ -68,13 +64,14 @@

          Functions

          - - - - + + + + @@ -89,31 +86,6 @@

          Functions

          -
          - - register (name, componentClass) -
          -
          - Registers a ComponentClass. - - -

          Parameters:

          -
            -
          • name - string - Name to register under -
          • -
          • componentClass - Component - ComponentClass to register -
          • -
          - - - - - -
          has (name) @@ -140,6 +112,37 @@ + +
          + + try (name) +
          +
          + Returns true and the ComponentClass if one was registered with the specified name + or false and an error otherwise + + +

          Parameters:

          +
            +
          • name + string + Name of the ComponentClass to check +
          • +
          + +

          Returns:

          +
            +
          1. + boolean +
          2. +
          3. + Component + or error string
          4. +
          + + + +
          @@ -175,7 +178,7 @@
          generated by LDoc 1.4.6 -Last updated 2020-01-04 10:27:07 +Last updated 2020-08-18 15:20:32
          From fd558dd3fec41a728f795fe54233ac0c142a4471 Mon Sep 17 00:00:00 2001 From: John Cheesman Date: Wed, 5 May 2021 18:32:04 +0100 Subject: [PATCH 071/113] Correct syntax in Systems example code --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f79dcf..c63b9f7 100644 --- a/README.md +++ b/README.md @@ -266,10 +266,10 @@ Systems can have multiple pools. ```lua -- Create a System -local mySystemClass = Concord.system(pool = {"position"}) -- Pool named 'pool' will contain all Entities with a position Component +local mySystemClass = Concord.system({pool = {"position"}}) -- Pool named 'pool' will contain all Entities with a position Component -- Create a System with multiple pools -local mySystemClass = Concord.system( +local mySystemClass = Concord.system({ pool = { -- This pool will be named 'pool' "position", "velocity", @@ -278,7 +278,7 @@ local mySystemClass = Concord.system( "health", "damageable", } -) +}) ``` ```lua From b53f950e3e1044805c201c9b32ab57a80818678d Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 16:30:23 +0100 Subject: [PATCH 072/113] Add documentation page building workflow --- .github/workflows/doc.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 0000000..a2215d3 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,29 @@ +name: Build Docs + +on: [push] + +jobs: + build: + name: Build docs + runs-on: ubuntu-latest + + steps: + - name: Setup Lua + uses: leafo/gh-actions-lua@v8 + with: + luaVersion: 5.4 + + - name: Setup Lua Rocks + uses: leafo/gh-actions-luarocks@v4 + + - name: Setup dependencies + run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec + + - name: Checkout & build docs + run: git clone --branch=build_docs https://github.com/lunarmodules/LDoc.git ldoc && cd ldoc && chmod +x build_versioned_docs.sh && ./build_versioned_docs.sh + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ldoc/doc_builds/ \ No newline at end of file From b701493a27b24d6c6c22a29b0aca04a19f7f6f72 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 16:32:24 +0100 Subject: [PATCH 073/113] Fix documentation page building workflow --- .github/workflows/doc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index a2215d3..3be2405 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -6,7 +6,7 @@ jobs: build: name: Build docs runs-on: ubuntu-latest - + steps: - name: Setup Lua uses: leafo/gh-actions-lua@v8 @@ -20,7 +20,7 @@ jobs: run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec - name: Checkout & build docs - run: git clone --branch=build_docs https://github.com/lunarmodules/LDoc.git ldoc && cd ldoc && chmod +x build_versioned_docs.sh && ./build_versioned_docs.sh + run: git clone https://github.com/lunarmodules/LDoc.git ldoc && cd ldoc && chmod +x build_versioned_docs.sh && ./build_versioned_docs.sh - name: Deploy uses: peaceiris/actions-gh-pages@v3 From 9aaff0fbccf94fba6ea65afb2acbfcec1e47e727 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 16:37:30 +0100 Subject: [PATCH 074/113] Fix documentation page building workflow 2 --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 3be2405..3f492b7 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -20,7 +20,7 @@ jobs: run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec - name: Checkout & build docs - run: git clone https://github.com/lunarmodules/LDoc.git ldoc && cd ldoc && chmod +x build_versioned_docs.sh && ./build_versioned_docs.sh + run: git clone https://github.com/lunarmodules/LDoc.git ldoc && lua ldoc/ldoc.lua . - name: Deploy uses: peaceiris/actions-gh-pages@v3 From 297b30aa505000b2f5d00724fc579cd3253b589a Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 16:55:04 +0100 Subject: [PATCH 075/113] Fix documentation page building workflow 3 --- .github/workflows/doc.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 3f492b7..037142f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -19,8 +19,14 @@ jobs: - name: Setup dependencies run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec - - name: Checkout & build docs - run: git clone https://github.com/lunarmodules/LDoc.git ldoc && lua ldoc/ldoc.lua . + - name: Check out repository code + uses: actions/checkout@v2 + + - name: Checkout LDoc & build docs + run: git clone https://github.com/lunarmodules/LDoc.git ldoc + + - name: Build docs + run: lua ldoc/ldoc.lua . - name: Deploy uses: peaceiris/actions-gh-pages@v3 From 65aae3c2babbdbd224e4b6d08c5159e70762a01d Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 16:57:08 +0100 Subject: [PATCH 076/113] Fix documentation page building workflow 4 --- .github/workflows/doc.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 037142f..f0bee72 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -23,10 +23,7 @@ jobs: uses: actions/checkout@v2 - name: Checkout LDoc & build docs - run: git clone https://github.com/lunarmodules/LDoc.git ldoc - - - name: Build docs - run: lua ldoc/ldoc.lua . + run: git clone https://github.com/lunarmodules/LDoc.git ldoc && lua ldoc/ldoc.lua . - name: Deploy uses: peaceiris/actions-gh-pages@v3 From c7625ad376093a2fc6a3a58188a81ffb9caf1462 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:02:38 +0100 Subject: [PATCH 077/113] Fix documentation page building workflow 5 --- .github/workflows/doc.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index f0bee72..cfb3d8e 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -22,11 +22,11 @@ jobs: - name: Check out repository code uses: actions/checkout@v2 - - name: Checkout LDoc & build docs + - name: Checkout & build docs run: git clone https://github.com/lunarmodules/LDoc.git ldoc && lua ldoc/ldoc.lua . - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ldoc/doc_builds/ \ No newline at end of file + # - name: Deploy + # uses: peaceiris/actions-gh-pages@v3 + # with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + # publish_dir: ldoc/doc_builds/ \ No newline at end of file From cdf425d301b5dd4d2cb80cc06b985d3cf0d674d8 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:08:25 +0100 Subject: [PATCH 078/113] Fix documentation page building workflow 6 --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index cfb3d8e..93af3e4 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v2 - name: Checkout & build docs - run: git clone https://github.com/lunarmodules/LDoc.git ldoc && lua ldoc/ldoc.lua . + run: git clone https://github.com/lunarmodules/LDoc.git ldoc && .lua/bin/lua ldoc/ldoc.lua . # - name: Deploy # uses: peaceiris/actions-gh-pages@v3 From 6329e09138c6df777c46800a12a9dabcdf4ee27e Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:16:51 +0100 Subject: [PATCH 079/113] Fix documentation page building workflow 7 --- .github/workflows/doc.yml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 93af3e4..aebf0c5 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -8,25 +8,29 @@ jobs: runs-on: ubuntu-latest steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Setup Lua uses: leafo/gh-actions-lua@v8 with: luaVersion: 5.4 - - name: Setup Lua Rocks + - name: Install Luarocks uses: leafo/gh-actions-luarocks@v4 - - name: Setup dependencies - run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec + - name: Install LDoc + run: luarocks install ldoc + + - name: Show + run: luarocks show ldoc - - name: Check out repository code - uses: actions/checkout@v2 + - name: Build docs + run: lua ldoc.lua . - - name: Checkout & build docs - run: git clone https://github.com/lunarmodules/LDoc.git ldoc && .lua/bin/lua ldoc/ldoc.lua . - # - name: Deploy - # uses: peaceiris/actions-gh-pages@v3 - # with: - # github_token: ${{ secrets.GITHUB_TOKEN }} - # publish_dir: ldoc/doc_builds/ \ No newline at end of file + # - name: Deploy + # uses: JamesIves/github-pages-deploy-action@4.1.5 + # with: + # branch: gh-pages + # folder: docs \ No newline at end of file From e141a6183bf5547bf43e842f8566425334f02e1c Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:18:57 +0100 Subject: [PATCH 080/113] Fix documentation page building workflow 8 --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index aebf0c5..ec4c9ec 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -26,7 +26,7 @@ jobs: run: luarocks show ldoc - name: Build docs - run: lua ldoc.lua . + run: ldoc . # - name: Deploy From 6ce714ce14f301ed3ba2cdf99bf14c479e49a429 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:21:22 +0100 Subject: [PATCH 081/113] Fix documentation page building workflow 9 --- .github/workflows/doc.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index ec4c9ec..4acd139 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -29,8 +29,8 @@ jobs: run: ldoc . - # - name: Deploy - # uses: JamesIves/github-pages-deploy-action@4.1.5 - # with: - # branch: gh-pages - # folder: docs \ No newline at end of file + - name: Deploy + uses: JamesIves/github-pages-deploy-action@4.1.5 + with: + branch: gh-pages + folder: docs \ No newline at end of file From 73177f4048a6bb02a2f18e4359a03be0e5cfba5a Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:21:51 +0100 Subject: [PATCH 082/113] Fix documentation page building workflow 10 --- .github/workflows/doc.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 4acd139..ee2d94e 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -28,9 +28,8 @@ jobs: - name: Build docs run: ldoc . - - - name: Deploy - uses: JamesIves/github-pages-deploy-action@4.1.5 - with: - branch: gh-pages - folder: docs \ No newline at end of file + - name: Deploy + uses: JamesIves/github-pages-deploy-action@4.1.5 + with: + branch: gh-pages + folder: docs \ No newline at end of file From 9c22986501e3777f2e7945080fa7af43c2c2dff3 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:23:53 +0100 Subject: [PATCH 083/113] Fix documentation page building workflow 11 --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index ee2d94e..8ac8c37 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -32,4 +32,4 @@ jobs: uses: JamesIves/github-pages-deploy-action@4.1.5 with: branch: gh-pages - folder: docs \ No newline at end of file + folder: concord/docs \ No newline at end of file From 4313dc78567f60394b4658cd0b7aea59a315a9b7 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 17:27:18 +0100 Subject: [PATCH 084/113] Fix documentation page building workflow 12 --- .github/workflows/doc.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 8ac8c37..5e5ae1e 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -1,6 +1,9 @@ name: Build Docs -on: [push] +on: + push: + branches: + - master jobs: build: From edc1d2fdbcb7776cc36a9d02d2a840dec548210a Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Tue, 2 Nov 2021 20:10:14 +0100 Subject: [PATCH 085/113] Fix documentation page building workflow 13 --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 5e5ae1e..ca60d46 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -35,4 +35,4 @@ jobs: uses: JamesIves/github-pages-deploy-action@4.1.5 with: branch: gh-pages - folder: concord/docs \ No newline at end of file + folder: docs \ No newline at end of file From 2acb1458f217bd828bb05246a4930bd685c6a7cc Mon Sep 17 00:00:00 2001 From: Josh Perry Date: Tue, 31 May 2022 20:20:54 +0100 Subject: [PATCH 086/113] Fixed docs link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f79dcf..aecc33c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ With Concord it is possibile to easily write fast and clean code. This readme will explain how to use Concord. Additionally all of Concord is documented using the LDoc format. -Auto generated docs for Concord can be found in `docs` folder, or on the [Github page](https://tjakka5.github.io/Concord/). +Auto generated docs for Concord can be found in `docs` folder, or on the [GitHub page](https://keyslam.github.io/Concord/). --- From 940870318d1225ed3106355f48fac13f0d21d35c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Mon, 17 Oct 2022 20:17:54 +0100 Subject: [PATCH 087/113] Fix bug where loading namespaces using folder/init.lua entries would only work if "folder" was 3 characters --- concord/utils.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/concord/utils.lua b/concord/utils.lua index da83c4d..92e19aa 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -34,11 +34,18 @@ function Utils.loadNamespace(pathOrFiles, namespace) local files = love.filesystem.getDirectoryItems(pathOrFiles) for _, file in ipairs(files) do - local name = file:sub(1, #file - 4) - local path = pathOrFiles.."."..name + local isFile = love.filesystem.getInfo(pathOrFiles .. "/" .. file).type == "file" - local value = require(path) - if namespace then namespace[name] = value end + if isFile then + local name = file:sub(1, #file - 4) + local path = pathOrFiles.."."..name + + local value = require(path) + if namespace then namespace[name] = value end + else + local value = require(pathOrFiles.."."..file) + if namespace then namespace[file] = value end + end end elseif (type(pathOrFiles == "table")) then for _, path in ipairs(pathOrFiles) do @@ -61,4 +68,4 @@ function Utils.loadNamespace(pathOrFiles, namespace) return namespace end -return Utils \ No newline at end of file +return Utils From 89eab3fb72597a1bef2d63e78fa4cf34810a549c Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:08 -0300 Subject: [PATCH 088/113] List.sort Fixes #33 --- concord/list.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/concord/list.lua b/concord/list.lua index 1263a72..88dfc04 100644 --- a/concord/list.lua +++ b/concord/list.lua @@ -94,6 +94,20 @@ function List:indexOf(obj) return self[obj] end +--- Sorts the List in place, using the order function. +-- The order function is passed to table.sort internally so documentation on table.sort can be used as reference. +-- @param order Function that takes two Entities (a and b) and returns true if a should go before than b. +-- @treturn List self +function List:sort(order) + table.sort(self, order) + + for key, obj in ipairs(self) do + self[obj] = key + end + + return self +end + return setmetatable(List, { __call = function() return List.new() From 695cc2dfe37853226090e101b41dedbddaf74edb Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:22 -0300 Subject: [PATCH 089/113] Add Component Negation Fixes #32 --- concord/component.lua | 4 ++++ concord/components.lua | 32 ++++++++++++++++++++++++++------ concord/pool.lua | 10 +++++++--- concord/system.lua | 25 ++++++++++++++----------- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/concord/component.lua b/concord/component.lua index d8678f0..7a9db98 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -19,6 +19,10 @@ function Component.new(name, populate) error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) end + if (string.match(name, Components.__REJECT_MATCH) ~= "") then + error("bad argument #1 to 'Component.new' (Component names can't start with '"..Components.__REJECT_PREFIX.."', got "..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 diff --git a/concord/components.lua b/concord/components.lua index bf20d2d..b2743e7 100644 --- a/concord/components.lua +++ b/concord/components.lua @@ -1,12 +1,11 @@ --- Container for registered ComponentClasses -- @module Components -local PATH = (...):gsub('%.[^%.]+$', '') - -local Type = require(PATH..".type") - local Components = {} +Components.__REJECT_PREFIX = "!" +Components.__REJECT_MATCH = "^(%"..Components.__REJECT_PREFIX.."?)(.+)" + --- Returns true if the containter has the ComponentClass with the specified name -- @string name Name of the ComponentClass to check -- @treturn boolean @@ -14,22 +13,43 @@ function Components.has(name) return rawget(Components, name) and true or false end +--- Prefix a component's name with the currently set Reject Prefix +-- @string name Name of the ComponentClass to reject +-- @treturn string +function Components.reject(name) + local ok, err = Components.try(name) + + if not ok then error(err, 2) end + + return Components.__REJECT_PREFIX..name +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 +-- @boolean acceptRejected Whether to accept names prefixed with the Reject Prefix. -- @treturn boolean -- @treturn Component or error string -function Components.try(name) +-- @treturn true if acceptRejected was true and the name had the Reject Prefix, false otherwise. +function Components.try(name, acceptRejected) if type(name) ~= "string" then return false, "ComponentsClass name is expected to be a string, got "..type(name)..")" end + local rejected = false + if acceptRejected then + local prefix + prefix, name = string.match(name, Components.__REJECT_MATCH) + + rejected = prefix ~= "" and name + end + local value = rawget(Components, name) if not value then return false, "ComponentClass '"..name.."' does not exist / was not registered" end - return true, value + return true, value, rejected end --- Returns the ComponentClass with the specified name diff --git a/concord/pool.lua b/concord/pool.lua index f1c80eb..f5cd401 100644 --- a/concord/pool.lua +++ b/concord/pool.lua @@ -30,10 +30,14 @@ end -- @tparam Entity e Entity to check -- @treturn boolean function Pool:eligible(e) - for i=#self.__filter, 1, -1 do - local component = self.__filter[i].__name + for i=#self.__filter.require, 1, -1 do + local name = self.__filter.require[i] + if not e[name] then return false end + end - if not e[component] then return false 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 diff --git a/concord/system.lua b/concord/system.lua index 756b124..fe0c9fe 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -33,7 +33,7 @@ System.mt = { Utils.shallowCopy(systemClass, system) end - for name, filter in pairs(systemClass.__filter) do + for name, filter in pairs(systemClass.__filters) do local pool = Pool(name, filter) system[name] = pool @@ -46,10 +46,10 @@ System.mt = { end, } -local validateFilters = function (baseFilters) +local validateFilters = function (definition) local filters = {} - for name, componentsList in pairs(baseFilters) do + 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 @@ -58,18 +58,19 @@ local validateFilters = function (baseFilters) error("invalid component list for filter '"..name.."' (table expected, got "..type(componentsList)..")", 3) end - local filter = {} + filters[name] = { require = {}, reject = {} } + for n, component in ipairs(componentsList) do - local ok, componentClass = Components.try(component) + 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 - - filter[#filter + 1] = componentClass end - - filters[name] = filter end return filters @@ -78,9 +79,11 @@ end --- Creates a new SystemClass. -- @param table filters A table containing filters (name = {components...}) -- @treturn System A new SystemClass -function System.new(filters) +function System.new(definition) + local filters = validateFilters(definition) + local systemClass = setmetatable({ - __filter = validateFilters(filters), + __filters = filters, __name = nil, __isSystemClass = true, From 50249d5ad334db4bc93ebe88293aabd3191a00f7 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:23 -0300 Subject: [PATCH 090/113] Removed deprecated functionality Removed hasName/getName on Systems and Worlds. Removed Entity.__components since it had a duplicate version of the components stored in the Entity itself. --- concord/entity.lua | 23 ++++++++++++----------- concord/system.lua | 13 ------------- concord/world.lua | 13 ------------- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/concord/entity.lua b/concord/entity.lua index 854fa8c..21a09cb 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -6,6 +6,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') local Components = require(PATH..".components") local Type = require(PATH..".type") +local Utils = require(PATH..".utils") local Entity = {} Entity.__mt = { @@ -22,7 +23,6 @@ function Entity.new(world) local e = setmetatable({ __world = nil, - __components = {}, __isEntity = true, }, Entity.__mt) @@ -38,14 +38,12 @@ local function give(e, name, componentClass, ...) local component = componentClass:__initialize(...) e[name] = component - e.__components[name] = component e:__dirty() end -local function remove(e, name, componentClass) +local function remove(e, name) e[name] = nil - e.__components[name] = nil e:__dirty() end @@ -76,7 +74,7 @@ function Entity:ensure(name, ...) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) + error("bad argument #1 to 'Entity:ensure' ("..componentClass..")", 2) end if self[name] then @@ -95,10 +93,10 @@ function Entity:remove(name) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) + error("bad argument #1 to 'Entity:remove' ("..componentClass..")", 2) end - remove(self, name, componentClass) + remove(self, name) return self end @@ -169,7 +167,11 @@ end -- Use Entity:give/ensure/remove instead -- @treturn table Table of all Components the Entity has function Entity:getComponents() - return self.__components + local components = Utils.shallowCopy(self) + components.__world = nil + components.__isEntity = nil + + return components end --- Returns true if the Entity is in a World. @@ -187,8 +189,8 @@ end function Entity:serialize() local data = {} - for _, component in pairs(self.__components) do - if component.__name then + for name, component in pairs(self) do + if name ~= "__world" and name ~= "__isEntity" and component.__name == name then local componentData = component:serialize() if componentData ~= nil then @@ -215,7 +217,6 @@ function Entity:deserialize(data) component:deserialize(componentData) self[componentData.__name] = component - self.__components[componentData.__name] = component self:__dirty() end diff --git a/concord/system.lua b/concord/system.lua index fe0c9fe..3db2a44 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -85,7 +85,6 @@ function System.new(definition) local systemClass = setmetatable({ __filters = filters, - __name = nil, __isSystemClass = true, }, System.mt) systemClass.__index = systemClass @@ -161,18 +160,6 @@ function System:getWorld() return self.__world end ---- Returns true if the System has a name. --- @treturn boolean -function System:hasName() - return self.__name and true or false -end - ---- Returns the name of the System. --- @treturn string -function System:getName() - return self.__name -end - --- Callbacks -- @section Callbacks diff --git a/concord/world.lua b/concord/world.lua index e874881..b55f030 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -34,7 +34,6 @@ function World.new() __systemLookup = {}, - __name = nil, __isWorld = true, }, World.__mt) @@ -349,18 +348,6 @@ function World:deserialize(data, append) self:__flush() end ---- Returns true if the World has a name. --- @treturn boolean -function World:hasName() - return self.__name and true or false -end - ---- Returns the name of the World. --- @treturn string -function World:getName() - return self.__name -end - --- Callback for when an Entity is added to the World. -- @tparam Entity e The Entity that was added function World:onEntityAdded(e) -- luacheck: ignore From c4594da19d0f154676415dbb8ad77ef53b1732b8 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:23 -0300 Subject: [PATCH 091/113] Add Component:removed() callback Fixes #37 I also added a reference to the Entity inside the Component which will help with #38 --- concord/component.lua | 20 ++++++++++++++------ concord/entity.lua | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/concord/component.lua b/concord/component.lua index 7a9db98..5122e9a 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -47,31 +47,39 @@ function Component.new(name, populate) return componentClass end --- Internal: Populates a Component with values +-- Internal: Populates a Component with values. function Component:__populate() -- luacheck: ignore end +-- Callback: When the Component gets removed or replaced in an Entity. +function Component:removed() -- luacheck: ignore +end + +-- Callback: When the Component gets serialized as part of an Entity. function Component:serialize() local data = Utils.shallowCopy(self, {}) --This values shouldn't be copied over - data.__componentClass = nil - data.__isComponent = nil + data.__componentClass = nil + data.__entity = nil + data.__isComponent = nil data.__isComponentClass = nil return data end +-- Callback: When the Component gets deserialized from serialized data. function Component:deserialize(data) Utils.shallowCopy(data, self) end -- Internal: Creates a new Component. -- @return A new Component -function Component:__new() +function Component:__new(entity) local component = setmetatable({ __componentClass = self, + __entity = entity, __isComponent = true, __isComponentClass = false, }, self.__mt) @@ -82,8 +90,8 @@ end -- Internal: Creates and populates a new Component. -- @param ... Varargs passed to the populate function -- @return A new populated Component -function Component:__initialize(...) - local component = self:__new() +function Component:__initialize(entity, ...) + local component = self:__new(entity) self.__populate(component, ...) diff --git a/concord/entity.lua b/concord/entity.lua index 21a09cb..7bdbdc2 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -35,17 +35,28 @@ function Entity.new(world) end local function give(e, name, componentClass, ...) - local component = componentClass:__initialize(...) + local component = componentClass:__initialize(e, ...) + local hadComponent = not not e[name] + + if hadComponent then + e[name]:removed() + end e[name] = component - e:__dirty() + if not hadComponent then + e:__dirty() + end end local function remove(e, name) - e[name] = nil + if e[name] then + e[name]:removed() - e:__dirty() + e[name] = nil + + e:__dirty() + end end --- Gives an Entity a Component. From 07bd5d0f280b14c51ed17536e9b66b753fb17193 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:23 -0300 Subject: [PATCH 092/113] Fix utils.loadNamespace when passed a table --- concord/utils.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/concord/utils.lua b/concord/utils.lua index 92e19aa..2d3d742 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -21,13 +21,13 @@ end -- @param namespace A table that will hold the required files -- @treturn table The namespace table function Utils.loadNamespace(pathOrFiles, namespace) - if (type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table") then + if type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table" then error("bad argument #1 to 'loadNamespace' (string/table of strings expected, got "..type(pathOrFiles)..")", 2) end - if (type(pathOrFiles) == "string") then + if type(pathOrFiles) == "string" then local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore - if (info == nil or info.type ~= "directory") then + if info == nil or info.type ~= "directory" then error("bad argument #1 to 'loadNamespace' (path '"..pathOrFiles.."' not found)", 2) end @@ -47,16 +47,16 @@ function Utils.loadNamespace(pathOrFiles, namespace) if namespace then namespace[file] = value end end end - elseif (type(pathOrFiles == "table")) then + elseif type(pathOrFiles) == "table" then for _, path in ipairs(pathOrFiles) do - if (type(path) ~= "string") then + if type(path) ~= "string" then error("bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing "..type(path)..")", 2) -- luacheck: ignore end local name = path local dotIndex, slashIndex = path:match("^.*()%."), path:match("^.*()%/") - if (dotIndex or slashIndex) then + if dotIndex or slashIndex then name = path:sub((dotIndex or slashIndex) + 1) end From 743d662ef9089bb4884d9d83c44c6e0feaeda0cf Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:23 -0300 Subject: [PATCH 093/113] 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 --- .gitignore | 5 +- concord/filter.lua | 168 +++++++++++++++++++++++++++++++++++++++++++++ concord/list.lua | 14 +++- concord/pool.lua | 115 ------------------------------- concord/system.lua | 66 ++++++------------ concord/type.lua | 16 +++++ 6 files changed, 221 insertions(+), 163 deletions(-) create mode 100644 concord/filter.lua delete mode 100644 concord/pool.lua diff --git a/.gitignore b/.gitignore index 51b8439..989d364 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ luac.out *.app *.i*86 *.x86_64 -*.hex \ No newline at end of file +*.hex + +# VSCode +.vscode/ \ No newline at end of file diff --git a/concord/filter.lua b/concord/filter.lua new file mode 100644 index 0000000..a959609 --- /dev/null +++ b/concord/filter.lua @@ -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, +}) \ No newline at end of file diff --git a/concord/list.lua b/concord/list.lua index 88dfc04..7f10c74 100644 --- a/concord/list.lua +++ b/concord/list.lua @@ -16,7 +16,7 @@ end --- Adds an object to the List. -- 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 -- @treturn List self function List:add(obj) @@ -26,6 +26,7 @@ function List:add(obj) self[obj] = size self.size = size + if self.onAdded then self:onAdded(obj) end return self end @@ -51,6 +52,7 @@ function List:remove(obj) self[obj] = nil self.size = size - 1 + if self.onRemoved then self:onRemoved(obj) end return self end @@ -108,6 +110,16 @@ function List:sort(order) return self 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, { __call = function() return List.new() diff --git a/concord/pool.lua b/concord/pool.lua deleted file mode 100644 index f5cd401..0000000 --- a/concord/pool.lua +++ /dev/null @@ -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, -}) diff --git a/concord/system.lua b/concord/system.lua index 3db2a44..2d3a802 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -5,9 +5,8 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Pool = require(PATH..".pool") +local Filter = require(PATH..".filter") local Utils = require(PATH..".utils") -local Components = require(PATH..".components") local System = { ENABLE_OPTIMIZATION = true, @@ -19,7 +18,7 @@ System.mt = { local system = setmetatable({ __enabled = true, - __pools = {}, + __filters = {}, __world = world, __isSystem = true, @@ -33,11 +32,11 @@ System.mt = { Utils.shallowCopy(systemClass, system) end - for name, filter in pairs(systemClass.__filters) do - local pool = Pool(name, filter) + for name, def in pairs(systemClass.__definition) do + local filter, pool = Filter(name, Utils.shallowCopy(def, {})) system[name] = pool - system.__pools[#system.__pools + 1] = pool + table.insert(system.__filters, filter) end system:init(world) @@ -45,45 +44,20 @@ System.mt = { return system 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. -- @param table filters A table containing filters (name = {components...}) -- @treturn System A new SystemClass 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({ - __filters = filters, + __definition = definition, __isSystemClass = true, }, System.mt) @@ -103,8 +77,8 @@ end -- @param e The Entity to check -- @treturn System self function System:__evaluate(e) - for _, pool in ipairs(self.__pools) do - pool:evaluate(e) + for _, filter in ipairs(self.__filters) do + filter:evaluate(e) end return self @@ -114,9 +88,9 @@ end -- @param e The Entity to remove -- @treturn System self function System:__remove(e) - for _, pool in ipairs(self.__pools) do - if pool:has(e) then - pool:remove(e) + for _, filter in ipairs(self.__filters) do + if filter:has(e) then + filter:remove(e) end end @@ -126,8 +100,8 @@ end -- Internal: Clears all Entities from the System. -- @treturn System self function System:__clear() - for i = 1, #self.__pools do - self.__pools[i]:clear() + for _, filter in ipairs(self.__filters) do + filter:clear() end return self diff --git a/concord/type.lua b/concord/type.lua index 157a8d4..bf7f52a 100644 --- a/concord/type.lua +++ b/concord/type.lua @@ -3,6 +3,15 @@ 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. -- @param t Object to check -- @treturn boolean @@ -45,4 +54,11 @@ function Type.isWorld(t) return type(t) == "table" and t.__isWorld or false 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 From 892f4d47000e72678a20518607bc5a14f2ae78fc Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:24 -0300 Subject: [PATCH 094/113] Error handling overhaul --- concord/component.lua | 8 ++++---- concord/entity.lua | 16 ++++++++-------- concord/filter.lua | 35 ++++++++++++++++++++++++++++++----- concord/system.lua | 2 +- concord/utils.lua | 10 +++++++--- concord/world.lua | 14 +++++++------- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/concord/component.lua b/concord/component.lua index 5122e9a..351d00c 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -16,19 +16,19 @@ Component.__mt = { -- @treturn Component A new ComponentClass function Component.new(name, populate) if (type(name) ~= "string") then - error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) + Utils.error(2, "bad argument #1 to 'Component.new' (string expected, got %s)", type(name)) end if (string.match(name, Components.__REJECT_MATCH) ~= "") then - error("bad argument #1 to 'Component.new' (Component names can't start with '"..Components.__REJECT_PREFIX.."', got "..name..")", 2) + Utils.error(2, "bad argument #1 to 'Component.new' (Component names can't start with '%s', got %s)", Components.__REJECT_PREFIX, name) end if (rawget(Components, name)) then - error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore + Utils.error(2, "bad argument #1 to 'Component.new' (ComponentClass with name '%s' was already registerd)", name) -- 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) + Utils.error(2, "bad argument #1 to 'Component.new' (function/nil expected, got %s)", type(populate)) end local componentClass = setmetatable({ diff --git a/concord/entity.lua b/concord/entity.lua index 7bdbdc2..fb4617c 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -18,7 +18,7 @@ Entity.__mt = { -- @treturn Entity A new Entity function Entity.new(world) if (world ~= nil and not Type.isWorld(world)) then - error("bad argument #1 to 'Entity.new' (world/nil expected, got "..type(world)..")", 2) + Utils.error(2, "bad argument #1 to 'Entity.new' (world/nil expected, got %s)", type(world)) end local e = setmetatable({ @@ -68,7 +68,7 @@ function Entity:give(name, ...) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) + Utils.error(2, "bad argument #1 to 'Entity:get' (%s)", componentClass) end give(self, name, componentClass, ...) @@ -85,7 +85,7 @@ function Entity:ensure(name, ...) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:ensure' ("..componentClass..")", 2) + Utils.error(2, "bad argument #1 to 'Entity:ensure' (%s)", componentClass) end if self[name] then @@ -104,7 +104,7 @@ function Entity:remove(name) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:remove' ("..componentClass..")", 2) + Utils.error(2, "bad argument #1 to 'Entity:remove' (%s)", componentClass) end remove(self, name) @@ -118,7 +118,7 @@ end -- @treturn Entity self function Entity:assemble(assemblage, ...) if type(assemblage) ~= "function" then - error("bad argument #1 to 'Entity:assemble' (function expected, got "..type(assemblage)..")") + Utils.error(2, "bad argument #1 to 'Entity:assemble' (function expected, got %s)", type(assemblage)) end assemblage(self, ...) @@ -154,7 +154,7 @@ function Entity:has(name) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:has' ("..componentClass..")", 2) + Utils.error(2, "bad argument #1 to 'Entity:has' (%s)", componentClass) end return self[name] and true or false @@ -167,7 +167,7 @@ function Entity:get(name) local ok, componentClass = Components.try(name) if not ok then - error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) + Utils.error(2, "bad argument #1 to 'Entity:get' (%s)", componentClass) end return self[name] @@ -219,7 +219,7 @@ function Entity:deserialize(data) local componentData = data[i] if (not Components.has(componentData.__name)) then - error("bad argument #1 to 'Entity:deserialize' (ComponentClass '"..tostring(componentData.__name).."' wasn't yet loaded)") -- luacheck: ignore + Utils.error(2, "bad argument #1 to 'Entity:deserialize' (ComponentClass '%s' wasn't yet loaded)", tostring(componentData.__name)) -- luacheck: ignore end local componentClass = Components[componentData.__name] diff --git a/concord/filter.lua b/concord/filter.lua index a959609..f0f9262 100644 --- a/concord/filter.lua +++ b/concord/filter.lua @@ -6,6 +6,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') local List = require(PATH..".list") local Type = require(PATH..".type") +local Utils = require(PATH..".utils") local Components = require(PATH..".components") @@ -20,18 +21,18 @@ Filter.__mt = { -- @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) + Utils.error(3, "invalid component list for filter '%s' (table expected, got %s)", name, type(def)) 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) + Utils.error(3, "invalid pool constructor for filter '%s' (callable expected, got %s)", name, type(def.constructor)) 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) + Utils.error(3, "invalid component for filter '%s' at position #%d (%s)", name, n, err) end if onComponent then @@ -61,14 +62,38 @@ function Filter.parse (name, def) return required, rejected end +local REQUIRED_METHODS = {"add", "remove", "has", "clear"} +local VALID_POOL_TYPES = {table=true, userdata=true, lightuserdata=true, cdata=true} + +function Filter.isValidPool (name, pool) + local poolType = type(pool) + --Check that pool is not nil + if not VALID_POOL_TYPES[poolType] then + Utils.error(3, "invalid value returned by pool '%s' constructor (table expected, got %s).", name, type(pool)) + end + + --Check if methods are callables + for _, method in ipairs(REQUIRED_METHODS) do + if not Type.isCallable(pool[method]) then + Utils.error(3, "invalid :%s method on pool '%s' (callable expected, got %s).", method, name, type(pool[method])) + end + end +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 pool + + if def.constructor then + pool = def.constructor(def) + Filter.isValidPool(name, pool) + else + pool = List() + end local required, rejected = Filter.parse(name, def) diff --git a/concord/system.lua b/concord/system.lua index 2d3a802..6ed6d6c 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -50,7 +50,7 @@ System.mt = { function System.new(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) + Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name)) end Filter.validate(name, def) diff --git a/concord/utils.lua b/concord/utils.lua index 2d3d742..1bf8710 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -3,6 +3,10 @@ local Utils = {} +function Utils.error(level, str, ...) + error(string.format(str, ...), level + 1) +end + --- Does a shallow copy of a table and appends it to a target table. -- @param orig Table to copy -- @param target Table to append to @@ -22,13 +26,13 @@ end -- @treturn table The namespace table function Utils.loadNamespace(pathOrFiles, namespace) if type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table" then - error("bad argument #1 to 'loadNamespace' (string/table of strings expected, got "..type(pathOrFiles)..")", 2) + Utils.error(2, "bad argument #1 to 'loadNamespace' (string/table of strings expected, got %s)", type(pathOrFiles)) end if type(pathOrFiles) == "string" then local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore if info == nil or info.type ~= "directory" then - error("bad argument #1 to 'loadNamespace' (path '"..pathOrFiles.."' not found)", 2) + Utils.error(2, "bad argument #1 to 'loadNamespace' (path '%s' not found)", pathOrFiles) end local files = love.filesystem.getDirectoryItems(pathOrFiles) @@ -50,7 +54,7 @@ function Utils.loadNamespace(pathOrFiles, namespace) elseif type(pathOrFiles) == "table" then for _, path in ipairs(pathOrFiles) do if type(path) ~= "string" then - error("bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing "..type(path)..")", 2) -- luacheck: ignore + Utils.error(2, "bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing %s)", type(path)) -- luacheck: ignore end local name = path diff --git a/concord/world.lua b/concord/world.lua index b55f030..bdd0af5 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -52,7 +52,7 @@ end -- @treturn World self function World:addEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) + Utils.error(2, "bad argument #1 to 'World:addEntity' (Entity expected, got %s)", type(e)) end if e.__world then @@ -70,7 +70,7 @@ end -- @treturn World self function World:removeEntity(e) if not Type.isEntity(e) then - error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) + Utils.error(2, "bad argument #1 to 'World:removeEntity' (Entity expected, got %s)", type(e)) end self.__removed:add(e) @@ -207,7 +207,7 @@ function World:addSystem(systemClass) local ok, err = tryAddSystem(self, systemClass) if not ok then - error("bad argument #1 to 'World:addSystem' ("..err..")", 2) + Utils.error(2, "bad argument #1 to 'World:addSystem' (%s)", err) end return self @@ -225,7 +225,7 @@ function World:addSystems(...) local ok, err = tryAddSystem(self, systemClass) if not ok then - error("bad argument #"..i.." to 'World:addSystems' ("..err..")", 2) + Utils.error(2, "bad argument #%d to 'World:addSystems' (%s)", i, err) end end @@ -237,7 +237,7 @@ end -- @treturn boolean function World:hasSystem(systemClass) if not Type.isSystemClass(systemClass) then - error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) + Utils.error(2, "bad argument #1 to 'World:hasSystem' (SystemClass expected, got %s)", type(systemClass)) end return self.__systemLookup[systemClass] and true or false @@ -248,7 +248,7 @@ end -- @treturn System System to get function World:getSystem(systemClass) if not Type.isSystemClass(systemClass) then - error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) + Utils.error(2, "bad argument #1 to 'World:getSystem' (SystemClass expected, got %s)", type(systemClass)) end return self.__systemLookup[systemClass] @@ -261,7 +261,7 @@ end -- @treturn World self function World:emit(functionName, ...) if not functionName or type(functionName) ~= "string" then - error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") + Utils.error(2, "bad argument #1 to 'World:emit' (String expected, got %s)", type(functionName)) end local shouldFlush = self.__emitSDepth == 0 From cc0fd1614ce421e6a640c71a3226446f230bea34 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:24 -0300 Subject: [PATCH 095/113] Serializable component You can remove the component to tell Concord an Entity shouldn't be serialized. It's given automatically on Entity creation, but this can be disabled by changing Entity.SERIALIZE_BY_DEFAULT to false. --- concord/builtins/init.lua | 5 +++++ concord/builtins/serializable.lua | 12 ++++++++++++ concord/entity.lua | 12 +++++++++++- concord/world.lua | 7 ++++--- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 concord/builtins/init.lua create mode 100644 concord/builtins/serializable.lua diff --git a/concord/builtins/init.lua b/concord/builtins/init.lua new file mode 100644 index 0000000..d5b749d --- /dev/null +++ b/concord/builtins/init.lua @@ -0,0 +1,5 @@ +local PATH = (...):gsub("(%.init)$", "") + +return { + serializable = require(PATH..".serializable"), +} diff --git a/concord/builtins/serializable.lua b/concord/builtins/serializable.lua new file mode 100644 index 0000000..125f76b --- /dev/null +++ b/concord/builtins/serializable.lua @@ -0,0 +1,12 @@ +local PATH = (...):gsub('%.builtins%.[^%.]+$', '') + +local Component = require(PATH..".component") + +local Serializable = Component("serializable") + +function Serializable:serialize () + -- Don't serialize this Component + return nil +end + +return Serializable \ No newline at end of file diff --git a/concord/entity.lua b/concord/entity.lua index fb4617c..ca972e9 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -8,7 +8,13 @@ local Components = require(PATH..".components") local Type = require(PATH..".type") local Utils = require(PATH..".utils") -local Entity = {} +-- Initialize built-in Components (as soon as possible) +local Builtins = require(PATH..".builtins") + +local Entity = { + SERIALIZE_BY_DEFAULT = true, +} + Entity.__mt = { __index = Entity, } @@ -31,6 +37,10 @@ function Entity.new(world) world:addEntity(e) end + if Entity.SERIALIZE_BY_DEFAULT then + e:give("serializable") + end + return e end diff --git a/concord/world.lua b/concord/world.lua index bdd0af5..71b3485 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -323,9 +323,10 @@ function World:serialize() for i = 1, self.__entities.size do local entity = self.__entities[i] - local entityData = entity:serialize() - - data[i] = entityData + if entity.serializable then + local entityData = entity:serialize() + table.insert(data, entityData) + end end return data From a4ae392341b49c32740ae5efffc5966c2ccb307c Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:24 -0300 Subject: [PATCH 096/113] World:newEntity A shortcut for Concord.entity(World) --- concord/world.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/concord/world.lua b/concord/world.lua index 71b3485..efc5a23 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -65,6 +65,12 @@ function World:addEntity(e) return self end +--- Creates a new Entity and adds it to the World. +-- @treturn Entity e the new Entity +function World:newEntity() + return Entity(self) +end + --- Removes an Entity from the World. -- @tparam Entity e Entity to remove -- @treturn World self From 3d195c790f22e04f21f9f285ecf6b1567c4372c7 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:24 -0300 Subject: [PATCH 097/113] Add World:query This method allows you to query the World in order to find a set of Entities that matches a specific Filter. --- concord/filter.lua | 54 +++++++++++++++++++++++++--------------------- concord/system.lua | 2 +- concord/world.lua | 22 +++++++++++++++++++ 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/concord/filter.lua b/concord/filter.lua index f0f9262..0c4dabf 100644 --- a/concord/filter.lua +++ b/concord/filter.lua @@ -19,20 +19,25 @@ Filter.__mt = { -- @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) +function Filter.validate (errorLevel, name, def, onComponent) + local filter = "World:query filter" + if name then + filter = ("filter '%s'"):format(name) + end + if type(def) ~= 'table' then - Utils.error(3, "invalid component list for filter '%s' (table expected, got %s)", name, type(def)) + Utils.error(3 + errorLevel, "invalid component list for %s (table expected, got %s)", filter, type(def)) end if not onComponent and def.constructor and not Type.isCallable(def.constructor) then - Utils.error(3, "invalid pool constructor for filter '%s' (callable expected, got %s)", name, type(def.constructor)) + Utils.error(3 + errorLevel, "invalid pool constructor for %s (callable expected, got %s)", filter, type(def.constructor)) end for n, component in ipairs(def) do local ok, err, reject = Components.try(component, true) if not ok then - Utils.error(3, "invalid component for filter '%s' at position #%d (%s)", name, n, err) + Utils.error(3 + errorLevel, "invalid component for %s at position #%d (%s)", filter, n, err) end if onComponent then @@ -49,17 +54,30 @@ end -- @treturn table required -- @treturn table rejected function Filter.parse (name, def) - local required, rejected = {}, {} + local filter = {} - Filter.validate(name, def, function (component, reject) + Filter.validate(1, name, def, function (component, reject) if reject then - table.insert(rejected, reject) + table.insert(filter, reject) + table.insert(filter, false) else - table.insert(required, component) + table.insert(filter, component) + table.insert(filter, true) end end) - return required, rejected + return filter +end + +function Filter.match (e, filter) + for i=#filter, 2, -2 do + local match = filter[i - 0] + local name = filter[i - 1] + + if (not e[name]) == match then return false end + end + + return true end local REQUIRED_METHODS = {"add", "remove", "has", "clear"} @@ -95,14 +113,12 @@ function Filter.new (name, def) pool = List() end - local required, rejected = Filter.parse(name, def) + local filter = Filter.parse(name, def) local filter = setmetatable({ pool = pool, - __required = required, - __rejected = rejected, - + __filter = filter, __name = name, __isFilter = true, @@ -115,17 +131,7 @@ end -- @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 + return Filter.match(e, self.__filter) end function Filter:evaluate (e) diff --git a/concord/system.lua b/concord/system.lua index 6ed6d6c..53731ff 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -53,7 +53,7 @@ function System.new(definition) Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name)) end - Filter.validate(name, def) + Filter.validate(0, name, def) end local systemClass = setmetatable({ diff --git a/concord/world.lua b/concord/world.lua index efc5a23..e4aa48d 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -6,6 +6,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') +local Filter = require(PATH..".filter") local Entity = require(PATH..".entity") local Type = require(PATH..".type") local List = require(PATH..".list") @@ -71,6 +72,27 @@ function World:newEntity() return Entity(self) end +function World:query(def, onMatch) + local filter = Filter.parse(nil, def) + + local list = nil + if not Type.isCallable(onMatch) then + list = type(onMatch) == "table" and onMatch or {} + end + + for _, e in ipairs(self.__entities) do + if Filter.match(e, filter) then + if list then + table.insert(list, e) + else + onMatch(e) + end + end + end + + return list +end + --- Removes an Entity from the World. -- @tparam Entity e Entity to remove -- @treturn World self From a55efd042a6275749ad589a78ee2383865d0d645 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:25 -0300 Subject: [PATCH 098/113] Entity's Keys You can now give the 'key' component to Entities. A key will be generated automatically and stored in Entity.key.value. You can then use this key to fetch the Entity from the World with World:getEntityByKey(key) The keys are generated with a generator function that can be overriden. --- concord/builtins/init.lua | 1 + concord/builtins/key.lua | 37 ++++++++++++++ concord/component.lua | 2 + concord/entity.lua | 18 ++++--- concord/system.lua | 2 + concord/world.lua | 103 ++++++++++++++++++++++++++++++++++---- 6 files changed, 148 insertions(+), 15 deletions(-) create mode 100644 concord/builtins/key.lua diff --git a/concord/builtins/init.lua b/concord/builtins/init.lua index d5b749d..e3ab37b 100644 --- a/concord/builtins/init.lua +++ b/concord/builtins/init.lua @@ -2,4 +2,5 @@ local PATH = (...):gsub("(%.init)$", "") return { serializable = require(PATH..".serializable"), + key = require(PATH..".key"), } diff --git a/concord/builtins/key.lua b/concord/builtins/key.lua new file mode 100644 index 0000000..f083bd4 --- /dev/null +++ b/concord/builtins/key.lua @@ -0,0 +1,37 @@ +local PATH = (...):gsub('%.builtins%.[^%.]+$', '') + +local Component = require(PATH..".component") + +local getKey = function (self, key) + local entity = self.__entity + + if not entity:inWorld() then + error("entity needs to belong to a world") + end + + local world = entity:getWorld() + + return world:__assignKey(entity, key) +end + +local Key = Component("key", function (self, key) + self.value = getKey(self, key) +end) + +function Key:deserialize (data) + self.value = getKey(self, data.value) +end + +function Key:removed (replaced) + if not replaced then + local entity = self.__entity + + if entity:inWorld() then + local world = entity:getWorld() + + return world:__clearKey(entity) + end + end +end + +return Key \ No newline at end of file diff --git a/concord/component.lua b/concord/component.lua index 351d00c..2b43779 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -74,6 +74,7 @@ function Component:deserialize(data) end -- Internal: Creates a new Component. +-- @param entity The Entity that will receive this Component. -- @return A new Component function Component:__new(entity) local component = setmetatable({ @@ -88,6 +89,7 @@ function Component:__new(entity) end -- Internal: Creates and populates a new Component. +-- @param entity The Entity that will receive this Component. -- @param ... Varargs passed to the populate function -- @return A new populated Component function Component:__initialize(entity, ...) diff --git a/concord/entity.lua b/concord/entity.lua index ca972e9..120146f 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -9,7 +9,8 @@ local Type = require(PATH..".type") local Utils = require(PATH..".utils") -- Initialize built-in Components (as soon as possible) -local Builtins = require(PATH..".builtins") +local Builtins = require(PATH..".builtins.init") --luacheck: ignore +-- Builtins is unused but the require already registers the Components local Entity = { SERIALIZE_BY_DEFAULT = true, @@ -49,7 +50,7 @@ local function give(e, name, componentClass, ...) local hadComponent = not not e[name] if hadComponent then - e[name]:removed() + e[name]:removed(true) end e[name] = component @@ -61,7 +62,7 @@ end local function remove(e, name) if e[name] then - e[name]:removed() + e[name]:removed(false) e[name] = nil @@ -207,11 +208,16 @@ function Entity:getWorld() return self.__world end -function Entity:serialize() +function Entity:serialize(ignoreKey) local data = {} for name, component in pairs(self) do - if name ~= "__world" and name ~= "__isEntity" and component.__name == name then + -- The key component needs to be treated separately. + if name == "key" and component.__name == "key" then + if not ignoreKey then + data.key = component.value + end + elseif (name ~= "__world") and (name ~= "__isEntity") and (component.__name == name) then local componentData = component:serialize() if componentData ~= nil then @@ -234,7 +240,7 @@ function Entity:deserialize(data) local componentClass = Components[componentData.__name] - local component = componentClass:__new() + local component = componentClass:__new(self) component:deserialize(componentData) self[componentData.__name] = component diff --git a/concord/system.lua b/concord/system.lua index 53731ff..e9d45d6 100644 --- a/concord/system.lua +++ b/concord/system.lua @@ -48,6 +48,8 @@ System.mt = { -- @param table filters A table containing filters (name = {components...}) -- @treturn System A new SystemClass function System.new(definition) + definition = definition or {} + for name, def in pairs(definition) do if type(name) ~= 'string' then Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name)) diff --git a/concord/world.lua b/concord/world.lua index e4aa48d..85a19e8 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -19,6 +19,12 @@ World.__mt = { __index = World, } +local defaultGenerator = function (state) + local current = state + state = state +1 + return string.format("%d", current), state +end + --- Creates a new World. -- @treturn World The new World function World.new() @@ -29,6 +35,13 @@ function World.new() __events = {}, __emitSDepth = 0, + __hash = { + state = -2^53, + generator = defaultGenerator, + keys = {}, + entities = {} + }, + __added = List(), __backAdded = List(), __removed = List(), __backRemoved = List(), __dirty = List(), __backDirty = List(), @@ -101,6 +114,14 @@ function World:removeEntity(e) Utils.error(2, "bad argument #1 to 'World:removeEntity' (Entity expected, got %s)", type(e)) end + if e.__world ~= self then + error("trying to remove an Entity from a World it doesn't belong to", 2) + end + + if e:has("key") then + e:remove("key") + end + self.__removed:add(e) return self @@ -343,16 +364,16 @@ function World:getSystems() return self.__systems end -function World:serialize() +function World:serialize(ignoreKeys) self:__flush() - local data = {} + local data = { generator = self.__hash.state } for i = 1, self.__entities.size do local entity = self.__entities[i] if entity.serializable then - local entityData = entity:serialize() + local entityData = entity:serialize(ignoreKeys) table.insert(data, entityData) end end @@ -360,21 +381,85 @@ function World:serialize() return data end -function World:deserialize(data, append) - if (not append) then +function World:deserialize(data, startClean, ignoreGenerator) + if startClean then self:clear() end + if (not ignoreGenerator) then + self.__hash.state = data.generator + end + + local entities = {} + for i = 1, #data do - local entityData = data[i] + local entity = Entity(self) - local entity = Entity() - entity:deserialize(entityData) + if data[i].key then + local component = Components.key:__new() + entity.key = component:deserialize(data[i].key) - self:addEntity(entity) + entity:__dirty() + end + + entities[i] = entity + end + + for i = 1, #data do + entity[i]:deserialize(data[i]) end self:__flush() + + return self +end + +function World:setKeyGenerator(generator, initialState) + if not Type.isCallable(generator) then + Utils.error(2, "bad argument #1 to 'World:setKeyGenerator' (function expected, got %s)", type(generator)) + end + + self.__hash.generator = generator + self.__hash.state = initialState + + return self +end + +function World:__clearKey(e) + local key = self.__hash.keys[e] + + if key then + self.__hash.keys[e] = nil + self.__hash.entities[key] = nil + end + + return self +end + +function World:__assignKey(e, key) + local hash = self.__hash + + if not key then + key = hash.keys[e] + if key then return key end + + key, hash.state = hash.generator(hash.state) + end + + if hash.entities[key] and hash.entities[key] ~= e then + Utils.error(4, "Trying to assign a key that is already taken (key: '%s').", key) + elseif hash.keys[e] and hash.keys[e] ~= key then + Utils.error(4, "Trying to assign more than one key to an Entity. (old: '%s', new: '%s')", hash.keys[e], key) + end + + hash.keys[e] = key + hash.entities[key] = e + + return key +end + +function World:getEntityByKey(key) + return self.__hash.entities[key] end --- Callback for when an Entity is added to the World. From 41fcfac6afa7466188b2b4a3bf982e563d178e80 Mon Sep 17 00:00:00 2001 From: flamendless Date: Tue, 14 Feb 2023 18:14:25 -0300 Subject: [PATCH 099/113] Fixed errors on World:deserialize --- concord/world.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/concord/world.lua b/concord/world.lua index 85a19e8..9113ee2 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -8,6 +8,7 @@ local PATH = (...):gsub('%.[^%.]+$', '') local Filter = require(PATH..".filter") local Entity = require(PATH..".entity") +local Components = require(PATH..".components") local Type = require(PATH..".type") local List = require(PATH..".list") local Utils = require(PATH..".utils") @@ -396,7 +397,7 @@ function World:deserialize(data, startClean, ignoreGenerator) local entity = Entity(self) if data[i].key then - local component = Components.key:__new() + local component = Components.key:__new(entity) entity.key = component:deserialize(data[i].key) entity:__dirty() @@ -406,7 +407,7 @@ function World:deserialize(data, startClean, ignoreGenerator) end for i = 1, #data do - entity[i]:deserialize(data[i]) + entities[i]:deserialize(data[i]) end self:__flush() From bdfe2523b02218a28ca938f529567418053a5a3e Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:14:25 -0300 Subject: [PATCH 100/113] Fix require indentation --- concord/world.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/concord/world.lua b/concord/world.lua index 9113ee2..fc26e19 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -6,12 +6,12 @@ local PATH = (...):gsub('%.[^%.]+$', '') -local Filter = require(PATH..".filter") -local Entity = require(PATH..".entity") +local Filter = require(PATH..".filter") +local Entity = require(PATH..".entity") local Components = require(PATH..".components") -local Type = require(PATH..".type") -local List = require(PATH..".list") -local Utils = require(PATH..".utils") +local Type = require(PATH..".type") +local List = require(PATH..".list") +local Utils = require(PATH..".utils") local World = { ENABLE_OPTIMIZATION = true, From 8e1b14d53b78e98b743944effcb33194bb629d65 Mon Sep 17 00:00:00 2001 From: Pablo Ariel Mayobre Date: Tue, 14 Feb 2023 18:18:41 -0300 Subject: [PATCH 101/113] Ignore non-lua files in Utils.loadNamespace Fixes #48 --- concord/utils.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/concord/utils.lua b/concord/utils.lua index 1bf8710..b33b719 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -40,15 +40,12 @@ function Utils.loadNamespace(pathOrFiles, namespace) for _, file in ipairs(files) do local isFile = love.filesystem.getInfo(pathOrFiles .. "/" .. file).type == "file" - if isFile then + if isFile and string.match(file, '%.lua$') ~= nil then local name = file:sub(1, #file - 4) local path = pathOrFiles.."."..name local value = require(path) if namespace then namespace[name] = value end - else - local value = require(pathOrFiles.."."..file) - if namespace then namespace[file] = value end end end elseif type(pathOrFiles) == "table" then From 61720312cbec9e456145976e74cf2be6667374ec Mon Sep 17 00:00:00 2001 From: flamendless Date: Tue, 14 Feb 2023 18:18:42 -0300 Subject: [PATCH 102/113] Added optional table for output for entity:getComponents --- concord/entity.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/concord/entity.lua b/concord/entity.lua index 120146f..803006c 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -188,8 +188,9 @@ end -- Warning: Do not modify this table. -- Use Entity:give/ensure/remove instead -- @treturn table Table of all Components the Entity has -function Entity:getComponents() - local components = Utils.shallowCopy(self) +function Entity:getComponents(output) + output = output or {} + local components = Utils.shallowCopy(self, output) components.__world = nil components.__isEntity = nil From 16c77c6a66ee85e2244bcea3cfee3e42a532765f Mon Sep 17 00:00:00 2001 From: flamendless Date: Tue, 14 Feb 2023 18:18:42 -0300 Subject: [PATCH 103/113] Fixed bug with serialization/deserialization --- concord/builtins/key.lua | 6 +++--- concord/world.lua | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/concord/builtins/key.lua b/concord/builtins/key.lua index f083bd4..7e571ed 100644 --- a/concord/builtins/key.lua +++ b/concord/builtins/key.lua @@ -19,7 +19,7 @@ local Key = Component("key", function (self, key) end) function Key:deserialize (data) - self.value = getKey(self, data.value) + self.value = getKey(self, data) end function Key:removed (replaced) @@ -28,10 +28,10 @@ function Key:removed (replaced) if entity:inWorld() then local world = entity:getWorld() - + return world:__clearKey(entity) end end end -return Key \ No newline at end of file +return Key diff --git a/concord/world.lua b/concord/world.lua index fc26e19..9303fe0 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -398,7 +398,8 @@ function World:deserialize(data, startClean, ignoreGenerator) if data[i].key then local component = Components.key:__new(entity) - entity.key = component:deserialize(data[i].key) + component:deserialize(data[i].key) + entity.key = component entity:__dirty() end From cf05cfc97260a60d78a3db47a1283c4e30f156d2 Mon Sep 17 00:00:00 2001 From: Pablo Mayobre Date: Tue, 14 Feb 2023 18:18:42 -0300 Subject: [PATCH 104/113] Add ability to clone components Fixes #51 --- concord/entity.lua | 89 +++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/concord/entity.lua b/concord/entity.lua index 803006c..041e58e 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -45,7 +45,7 @@ function Entity.new(world) return e end -local function give(e, name, componentClass, ...) +local function createComponent(e, name, componentClass, ...) local component = componentClass:__initialize(e, ...) local hadComponent = not not e[name] @@ -60,7 +60,57 @@ local function give(e, name, componentClass, ...) end end -local function remove(e, name) +local function deserializeComponent(e, name, componentData) + local componentClass = Components[name] + local hadComponent = not not e[name] + + if hadComponent then + e[name]:removed(true) + end + + local component = componentClass:__new(e) + component:deserialize(componentData) + + e[name] = component + + if not hadComponent then + e:__dirty() + end +end + +local function giveComponent(e, ensure, name, ...) + local component + if Type.isComponent(name) then + component = name + name = component:getName() + end + + if ensure and e[name] then + return e + end + + local ok, componentClass = Components.try(name) + + if not ok then + Utils.error(3, "bad argument #1 to 'Entity:%s' (%s)", ensure and 'ensure' or 'give', componentClass) + end + + if component then + local data = component:deserialize() + if data == nil then + Utils.error(3, "bad argument #1 to 'Entity:$s' (Component '%s' couldn't be deserialized)", ensure and 'ensure' or 'give', name) + end + + deserializeComponent(e, name, data) + else + createComponent(e, name, componentClass, ...) + end + + return e +end + + +local function removeComponent(e, name) if e[name] then e[name]:removed(false) @@ -76,15 +126,7 @@ end -- @param ... additional arguments to pass to the Component's populate function -- @treturn Entity self function Entity:give(name, ...) - local ok, componentClass = Components.try(name) - - if not ok then - Utils.error(2, "bad argument #1 to 'Entity:get' (%s)", componentClass) - end - - give(self, name, componentClass, ...) - - return self + return giveComponent(self, false, name, ...) end --- Ensures an Entity to have a Component. @@ -93,19 +135,7 @@ end -- @param ... additional arguments to pass to the Component's populate function -- @treturn Entity self function Entity:ensure(name, ...) - local ok, componentClass = Components.try(name) - - if not ok then - Utils.error(2, "bad argument #1 to 'Entity:ensure' (%s)", componentClass) - end - - if self[name] then - return self - end - - give(self, name, componentClass, ...) - - return self + return giveComponent(self, true, name, ...) end --- Removes a Component from an Entity. @@ -118,7 +148,7 @@ function Entity:remove(name) Utils.error(2, "bad argument #1 to 'Entity:remove' (%s)", componentClass) end - remove(self, name) + removeComponent(self, name) return self end @@ -239,14 +269,7 @@ function Entity:deserialize(data) Utils.error(2, "bad argument #1 to 'Entity:deserialize' (ComponentClass '%s' wasn't yet loaded)", tostring(componentData.__name)) -- luacheck: ignore end - local componentClass = Components[componentData.__name] - - local component = componentClass:__new(self) - component:deserialize(componentData) - - self[componentData.__name] = component - - self:__dirty() + deserializeComponent(self, componentData.__name, componentData) end end From 9bccd0501965044b82c459c614c54b23dc1dfbdb Mon Sep 17 00:00:00 2001 From: Pablo Mayobre Date: Tue, 14 Feb 2023 18:18:43 -0300 Subject: [PATCH 105/113] Usability improvements - Now entity.key() is the same as entity.key.value - Entity:serialize only serializes component given correctly - Any other value inside the Entity is ignored - Disable some diagnostics used in Lua language server by sumneko --- .luacheckrc | 1 + concord/builtins/key.lua | 4 ++++ concord/component.lua | 1 + concord/entity.lua | 3 ++- concord/world.lua | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.luacheckrc b/.luacheckrc index b557738..59fc4be 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1 +1,2 @@ +---@diagnostic disable: lowercase-global std="love+luajit" diff --git a/concord/builtins/key.lua b/concord/builtins/key.lua index 7e571ed..7b23275 100644 --- a/concord/builtins/key.lua +++ b/concord/builtins/key.lua @@ -22,6 +22,10 @@ function Key:deserialize (data) self.value = getKey(self, data) end +function Key.__mt:__call() + return self.value +end + function Key:removed (replaced) if not replaced then local entity = self.__entity diff --git a/concord/component.lua b/concord/component.lua index 2b43779..981b999 100644 --- a/concord/component.lua +++ b/concord/component.lua @@ -95,6 +95,7 @@ end function Component:__initialize(entity, ...) local component = self:__new(entity) + ---@diagnostic disable-next-line: redundant-parameter self.__populate(component, ...) return component diff --git a/concord/entity.lua b/concord/entity.lua index 041e58e..29145fa 100644 --- a/concord/entity.lua +++ b/concord/entity.lua @@ -248,7 +248,8 @@ function Entity:serialize(ignoreKey) if not ignoreKey then data.key = component.value end - elseif (name ~= "__world") and (name ~= "__isEntity") and (component.__name == name) then + --We only care about components that were properly given to the entity + elseif Type.isComponent(component) and (component.__name == name) then local componentData = component:serialize() if componentData ~= nil then diff --git a/concord/world.lua b/concord/world.lua index 9303fe0..1f0c714 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -476,6 +476,7 @@ end return setmetatable(World, { __call = function(_, ...) + ---@diagnostic disable-next-line: redundant-parameter return World.new(...) end, }) From 429a448ab60edea1a9aa78f9b3345f7ad329d97d Mon Sep 17 00:00:00 2001 From: Jesse Viikari Date: Tue, 14 Feb 2023 18:18:43 -0300 Subject: [PATCH 106/113] Add resources to world - setResource(name, resource) to set a resource - getResource(name) to retrieve it --- concord/world.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/concord/world.lua b/concord/world.lua index 1f0c714..7008da4 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -36,6 +36,8 @@ function World.new() __events = {}, __emitSDepth = 0, + __resources = {}, + __hash = { state = -2^53, generator = defaultGenerator, @@ -474,6 +476,22 @@ end function World:onEntityRemoved(e) -- luacheck: ignore end +--- Sets a named resource in the world +-- @string name Name of the resource +-- @tparam Any resource Resource to set +-- @treturn World self +function World:setResource(name, resource) + self.__resources[name] = resource + return self +end + +--- Gets a named resource from the world +-- @string name Name of the resource +-- @treturn Any resource +function World:getResource(name) + return self.__resources[name] +end + return setmetatable(World, { __call = function(_, ...) ---@diagnostic disable-next-line: redundant-parameter From 1e4132be21b56e47f878251bf6d02652e46222dd Mon Sep 17 00:00:00 2001 From: Brandon Blanker Lim-it Date: Tue, 14 Feb 2023 18:18:43 -0300 Subject: [PATCH 107/113] Added beforeEmit and afterEmit World callbacks (#54) * Added beforeEmit and afterEmit World callbacks * Fixed beforeEmit/afterEmit to handle recursive/nested emits; * Added preventDefaults in beforeEmit --- concord/world.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/concord/world.lua b/concord/world.lua index 7008da4..7b31a6e 100644 --- a/concord/world.lua +++ b/concord/world.lua @@ -52,6 +52,8 @@ function World.new() __systemLookup = {}, __isWorld = true, + + __ignoreEmits = false }, World.__mt) -- Optimization: We deep copy the World class into our instance of a world. @@ -320,7 +322,14 @@ function World:emit(functionName, ...) self.__emitSDepth = self.__emitSDepth + 1 - local listeners = self.__events[functionName] + local listeners = self.__events[functionName] + + if not self.__ignoreEmits and Type.isCallable(self.beforeEmit) then + self.__ignoreEmits = true + local preventDefaults = self:beforeEmit(functionName, listeners, ...) + self.__ignoreEmits = false + if preventDefaults then return end + end if listeners then for i = 1, #listeners do @@ -336,6 +345,12 @@ function World:emit(functionName, ...) end end + if not self.__ignoreEmits and Type.isCallable(self.afterEmit) then + self.__ignoreEmits = true + self:afterEmit(functionName, listeners, ...) + self.__ignoreEmits = false + end + self.__emitSDepth = self.__emitSDepth - 1 return self From 2386547caa1a0602394a2a2c151717ce9400a95d Mon Sep 17 00:00:00 2001 From: Pablo Mayobre Date: Tue, 14 Feb 2023 22:28:26 -0300 Subject: [PATCH 108/113] Normalize slashes to dots when calling require in Utils.loadNamespace Co-authored-by: Ulhar --- concord/utils.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concord/utils.lua b/concord/utils.lua index b33b719..0802586 100644 --- a/concord/utils.lua +++ b/concord/utils.lua @@ -44,7 +44,7 @@ function Utils.loadNamespace(pathOrFiles, namespace) local name = file:sub(1, #file - 4) local path = pathOrFiles.."."..name - local value = require(path) + local value = require(path:gsub("%/", ".")) if namespace then namespace[name] = value end end end @@ -61,7 +61,7 @@ function Utils.loadNamespace(pathOrFiles, namespace) name = path:sub((dotIndex or slashIndex) + 1) end - local value = require(path) + local value = require(path:gsub("%/", ".")) if namespace then namespace[name] = value end end end From 5f4b3b97dab5fe4aa3bd387348b6bd20c5af9389 Mon Sep 17 00:00:00 2001 From: Daniel Power Date: Sat, 18 Feb 2023 16:43:56 -0330 Subject: [PATCH 109/113] Fix link to documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7872c69..f7211e4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ With Concord it is possibile to easily write fast and clean code. This readme will explain how to use Concord. Additionally all of Concord is documented using the LDoc format. -Auto generated docs for Concord can be found in `docs` folder, or on the [GitHub page](https://keyslam.github.io/Concord/). +Auto generated docs for Concord can be found in `docs` folder, or on the [GitHub page](https://keyslam-group.github.io/Concord/). --- From f9da8dbe928902934a77e1b0b56f8154ddbfff9f Mon Sep 17 00:00:00 2001 From: lambtoken Date: Sat, 23 Dec 2023 09:37:03 +0100 Subject: [PATCH 110/113] Added missing do keyword in the loops --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f7211e4..8f4d7b5 100644 --- a/README.md +++ b/README.md @@ -294,14 +294,14 @@ end -- Defining a function function mySystemClass:update(dt) -- Iterate over all entities in the Pool - for _, e in ipairs(self.pool) + for _, e in ipairs(self.pool) do -- Do something with the Components e.position.x = e.position.x + e.velocity.x * dt e.position.y = e.position.y + e.velocity.y * dt end -- Iterate over all entities in the second Pool - for _, e in ipairs(self.secondPool) + for _, e in ipairs(self.secondPool) do -- Do something end end From 1aaf50140135e5793b362a862cd15e8f0cfc52f0 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Mon, 27 May 2024 13:53:55 +0200 Subject: [PATCH 111/113] Update README.md to hint that components should be loaded for systems --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f7211e4..168db64 100644 --- a/README.md +++ b/README.md @@ -133,16 +133,16 @@ Concord does a few things that might not be immediately clear. This segment shou Since you'll have lots of Components and Systems in your game Concord makes it a bit easier to load things in. ```lua +-- Loads all files in the directory. Components automatically register into Concord.components, so loading them into a namespace isn't necessary. +Concord.utils.loadNamespace("path/to/components") + +print(Concord.components.componentName) + -- Loads all files in the directory, and puts the return value in the table Systems. The key is their filename without any extension local Systems = {} Concord.utils.loadNamespace("path/to/systems", Systems) print(Systems.systemName) - --- Loads all files in the directory. Components automatically register into Concord.components, so loading them into a namespace isn't necessary. -Concord.utils.loadNamespace("path/to/components") - -print(Concord.components.componentName) ``` #### Method chaining From 6575686b3bb5abfb2e9c477762c2de38f22f40a5 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sat, 29 Jun 2024 10:28:12 +0200 Subject: [PATCH 112/113] Fix typo in README.md A piece of example code was missing a `,`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 168db64..987c030 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ When defining a ComponentClass you need to pass in a name and usually a `populat -- Create the position class with a populate function -- The component variable is the actual Component given to an Entity -- The x and y variables are values we pass in when we create the Component -Concord.component("position" function(component, x, y) +Concord.component("position", function(component, x, y) component.x = x or 0 component.y = y or 0 end) From 848652f68887db0c4261efe499facbec88959d03 Mon Sep 17 00:00:00 2001 From: Justin van der Leij Date: Sun, 30 Jun 2024 10:45:46 +0200 Subject: [PATCH 113/113] Make example for how to use Concord.utils.loadNamespace for systems more concrete --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8db02a4..6669d8e 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,12 @@ print(Concord.components.componentName) local Systems = {} Concord.utils.loadNamespace("path/to/systems", Systems) -print(Systems.systemName) +myWorld:addSystems( + Systems.healthSystem + Systems.damageSystem, + Systems.moveSystem, + -- etc +) ``` #### Method chaining
          register (name, componentClass)Registers a ComponentClass.
          has (name) Returns true if the containter has the ComponentClass with the specified name
          try (name)Returns true and the ComponentClass if one was registered with the specified name + or false and an error otherwise
          get (name)