Rename 'lib' directory to 'src'

This commit is contained in:
Justin van der Leij 2018-11-26 12:45:24 +01:00
parent 39ec2106b7
commit 89a3a7fa8a
9 changed files with 0 additions and 0 deletions

26
src/assemblage.lua Normal file
View file

@ -0,0 +1,26 @@
--- Assemblage
local Assemblage = {}
Assemblage.__index = Assemblage
function Assemblage.new(assemble)
local assemblage = setmetatable({
__assemble = assemble,
__isAssemblage = true,
}, Assemblage)
return assemblage
end
function Assemblage:assemble(e, ...)
self.__assemble(e, ...)
return self
end
return setmetatable(Assemblage, {
__call = function(_, ...)
return Assemblage.new(...)
end,
})

39
src/component.lua Normal file
View file

@ -0,0 +1,39 @@
--- Component
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(populate)
local component = setmetatable({
__populate = populate,
__isComponent = true,
}, Component)
component.__mt = {__index = component}
return component
end
--- Creates and initializes a new Bag.
-- @param ... The values passed to the populate function
-- @return A new initialized Bag
function Component:__initialize(...)
if self.__populate then
local bag = setmetatable({}, self.__mt)
self.__populate(bag, ...)
return bag
end
return true
end
return setmetatable(Component, {
__call = function(_, ...)
return Component.new(...)
end,
})

142
src/entity.lua Normal file
View file

@ -0,0 +1,142 @@
--- Entity
local PATH = (...):gsub('%.[^%.]+$', '')
local Type = require(PATH..".type")
local List = require(PATH..".list")
local Entity = {}
Entity.__index = Entity
--- Creates and initializes a new Entity.
-- @return A new Entity
function Entity.new()
local e = setmetatable({
removed = {},
instances = List(),
__isEntity = true,
}, Entity)
return e
end
local function give(e, component, ...)
local comp = component:__initialize(...)
e[component] = comp
e:mark()
end
--- Gives an Entity a component with values.
-- @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)
end
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
--- 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)..")")
end
self.removed[#self.removed + 1] = component
self:mark()
return self
end
function Entity:assemble(assemblage, ...)
if not Type.isAssemblage(assemblage) then
error("bad argument #1 to 'Entity:assemble' (Assemblage expected, got "..type(assemblage)..")")
end
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[i] = nil
end
return self
end
--- Destroys the Entity.
-- @return self
function Entity:destroy()
for i = 1, self.instances.size do
self.instances:get(i):removeEntity(self)
end
return self
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)..")")
end
return self[component]
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)..")")
end
return self[component] ~= nil
end
return setmetatable(Entity, {
__call = function(_, ...)
return Entity.new(...)
end,
})

40
src/init.lua Normal file
View file

@ -0,0 +1,40 @@
--- init
local PATH = (...):gsub('%.init$', '')
local Concord = {
_VERSION = "1.0",
_DESCRIPTION = "A feature-complete ECS library",
_LICENCE = [[
MIT LICENSE
Copyright (c) 2018 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
"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.
]]
}
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

288
src/instance.lua Normal file
View file

@ -0,0 +1,288 @@
--- Instance
local PATH = (...):gsub('%.[^%.]+$', '')
local Type = require(PATH..".type")
local List = require(PATH..".list")
local Instance = {}
Instance.__index = Instance
--- Creates a new Instance.
-- @return The new instance
function Instance.new()
local instance = setmetatable({
entities = List(),
systems = List(),
events = {},
marked = {},
removed = {},
__isInstance = true,
}, Instance)
return instance
end
--- Adds an Entity to the Instance.
-- @param e The Entity to add
-- @return self
function Instance:addEntity(e)
if not Type.isEntity(e) then
error("bad argument #1 to 'Instance:addEntity' (Entity expected, got "..type(e)..")", 2)
end
self:onEntityAdded(e)
e.instances:add(self)
self.entities:add(e)
self: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
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
function Instance:checkEntity(e)
if not Type.isEntity(e) then
error("bad argument #1 to 'Instance: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 Instance.
-- @return self
function Instance:flush()
while #self.marked > 0 do
local marked = self.removed
self.removed = {}
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 j = 1, self.systems.size do
self.systems:get(j):__remove(e)
end
self:onEntityRemoved(e)
end
end
for i = 1, self.systems.size do
local system = self.systems:get(i)
system:flush()
system:clear()
end
return self
end
--- Adds a System to the Instance.
-- @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)
if not Type.isSystem(system) then
error("bad argument #1 to 'Instance:addSystem' (System expected, got "..type(system)..")", 2)
end
if system.__instance and system.__instance ~= self then
error("System already in instance '" ..tostring(system.__instance).."'")
end
if not self.systems:has(system) then
self.systems:add(system)
system.__instance = self
system:addedTo(self)
end
if eventName then
self.events[eventName] = self.events[eventName] or {}
local i = #self.events[eventName] + 1
self.events[eventName][i] = {
system = system,
callback = callback or eventName,
enabled = enabled == nil or enabled,
}
if enabled == nil or enabled then
system:enabledCallback(callback or eventName)
end
end
local e
for i = 1, self.entities.size do
e = self.entities:get(i)
self:checkEntity(e)
end
return self
end
--- Enables a System in the Instance.
-- @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)
if not Type.isSystem(system) then
error("bad argument #1 to 'Instance:enableSystem' (System expected, got "..type(system)..")", 2)
end
return self:setSystem(system, eventName, callback, true)
end
--- Disables a System in the Instance.
-- @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)
if not Type.isSystem(system) then
error("bad argument #1 to 'Instance:disableSystem' (System expected, got "..type(system)..")", 2)
end
return self:setSystem(system, eventName, callback, false)
end
--- Sets a System 'enable' in the Instance.
-- @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)
if not Type.isSystem(system) then
error("bad argument #1 to 'Instance:setSystem' (System expected, got "..type(system)..")", 2)
end
callback = callback or eventName
if callback then
local listeners = self.events[eventName]
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 Instance.
-- @param eventName The Event that should be emitted
-- @param ... Parameters passed to listeners
-- @return self
function Instance:emit(eventName, ...)
if not eventName or type(eventName) ~= "string" then
error("bad argument #1 to 'Instance:emit' (String expected, got "..type(eventName)..")")
end
self:flush()
local listeners = self.events[eventName]
if listeners then
for i = 1, #listeners do
local listener = listeners[i]
if listener.enabled then
listener.system[listener.callback](listener.system, ...)
end
end
end
return self
end
--- Removes all entities from the Instance
-- @return self
function Instance:clear()
for i = 1, self.entities.size do
self.entities:get(i):destroy()
end
self:flush()
return self
end
--- Default callback for adding an Entity.
-- @param e The Entity that was added
function Instance:onEntityAdded(e) -- luacheck: ignore
end
--- Default callback for removing an Entity.
-- @param e The Entity that was removed
function Instance:onEntityRemoved(e) -- luacheck: ignore
end
return setmetatable(Instance, {
__call = function(_, ...)
return Instance.new(...)
end,
})

83
src/list.lua Normal file
View file

@ -0,0 +1,83 @@
--- List
local List = {}
local 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
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"
local size = self.size + 1
self[size] = obj
self[obj] = size
self.size = size
return self
end
--- Removes an object from the List.
-- @param obj The object to remove
-- @return self
function List:remove(obj)
local index = self[obj]
if not index then return end
local size = self.size
if index == size then
self[size] = nil
else
local other = self[size]
self[index] = other
self[other] = index
self[size] = nil
end
self[obj] = nil
self.size = size - 1
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
function List:has(obj)
return self[obj] and true
end
return setmetatable(List, {
__call = function()
return List.new()
end,
})

52
src/pool.lua Normal file
View file

@ -0,0 +1,52 @@
--- Pool
local PATH = (...):gsub('%.[^%.]+$', '')
local List = require(PATH..".list")
local Pool = {}
Pool.__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)
pool.added = {}
pool.removed = {}
pool.name = name
pool.filter = filter
pool.__isPool = true
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
return false
end
end
return true
end
return setmetatable(Pool, {
__index = List,
__call = function(_, ...)
return Pool.new(...)
end,
})

162
src/system.lua Normal file
View file

@ -0,0 +1,162 @@
--- System
local PATH = (...):gsub('%.[^%.]+$', '')
local Pool = require(PATH..".pool")
local System = {}
System.mt = {
__index = System,
__call = function(systemProto, ...)
local system = setmetatable({
__all = {},
__pools = {},
__instance = nil,
__isSystem = true,
}, systemProto)
for _, filter in pairs(systemProto.__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
end
system:init(...)
return system
end,
}
--- Creates a new System prototype.
-- @param ... Variable amounts of filters
-- @return A new System prototype
function System.new(...)
local systemProto = setmetatable({
__filter = {...},
}, System.mt)
systemProto.__index = systemProto
return systemProto
end
--- Builds a Pool for the System.
-- @param baseFilter The 'raw' Filter
-- @return A new Pool
function System:__buildPool(baseFilter) -- luacheck: ignore
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
end
end
return Pool(name, filter)
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)
for _, pool in ipairs(self.__pools) do
local poolHas = pool:has(e)
local eligible = pool:eligible(e)
if not poolHas and eligible then
pool:add(e)
pool.added[#pool.added + 1] = e
self:__tryAdd(e)
elseif poolHas and not eligible then
pool:remove(e)
pool.removed[#pool.removed + 1] = e
self:__tryRemove(e)
end
end
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
end
end
end
function System:flush() -- luacheck: ignore
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
--- Default callback for system initialization.
-- @param ... Varags
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
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
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
end
return setmetatable(System, {
__call = function(_, ...)
return System.new(...)
end,
})

25
src/type.lua Normal file
View file

@ -0,0 +1,25 @@
-- Type
local Type = {}
function Type.isComponent(t)
return type(t) == "table" and t.__isComponent
end
function Type.isEntity(t)
return type(t) == "table" and t.__isEntity
end
function Type.isSystem(t)
return type(t) == "table" and t.__isSystem
end
function Type.isInstance(t)
return type(t) == "table" and t.__isInstance
end
function Type.isAssemblage(t)
return type(t) == "table" and t.__isAssemblage
end
return Type