math.randomseed(os.time()) local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) --===== EntityDb =====-- -- EntityDb is a database of entities and their associated components -- it should be quite efficient to query for all entities with a given component, and reasonably -- efficient to query for all components of a given entity EntityDb = {} EntityDb.__index = EntityDb function EntityDb.new(_) local self = { entities = {}, components = {}, } setmetatable(self, EntityDb) return self end setmetatable(EntityDb, {__call=EntityDb.new}) -- check if a given entity id is legitimate function EntityDb.checkIsValid(self, id) if not self.entities[id] then error(string.format("invalid entity id: %s", tostring(id))) end end local random = math.random local function uuid() local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' return string.gsub(template, '[xy]', function (c) local v = (c == 'x') and random(0, 0xf) or random(8, 0xb) return string.format('%x', v) end) end -- create a new entity function EntityDb.createEntity(self, id) local id = id or uuid() -- allow inserting entities at preset ids for loading self.entities[id] = true return id end -- add a component to an entity function EntityDb.addComponent(self, id, name, value) self:checkIsValid(id) -- create the relevant component table if it doesn't exist if not self.components[name] then self.components[name] = { count=0, data={} } end local component = self.components[name] component.data[id] = value component.count = component.count + 1 end -- get all entities with a given component function EntityDb.queryComponent(self, name) return self.components[name].data end -- get all components associated with an entity function EntityDb.queryEntity(self, id) self:checkIsValid(id) local query = {} for name, component in pairs(self.components) do query[name] = component.data[id] end return query end -- get a specific component from an entity function EntityDb.getComponent(self, id, name) self:checkIsValid(id) return self.components[name].data[id] end -- remove a component from an entity function EntityDb.removeComponent(self, id, name) self:checkIsValid(id) local component = self.components[name] if component.data[id] ~= nil then component.data[id] = nil component.count = component.count - 1 if component.count == 0 then self.components[name] = nil end end end -- remove an entity from the db function EntityDb.deleteEntity(self, id) self:checkIsValid(id) for name in pairs(self.components) do self:removeComponent(id, name) end self.entities[id] = nil end --===== SystemDb =====-- SystemDb = {} SystemDb.__index = SystemDb function SystemDb.new(_, entityDb) local self = { systems = {}, sorted = {}, entityDb = entityDb, } setmetatable(self, SystemDb) return self end setmetatable(SystemDb, {__call=SystemDb.new}) local function systemId() local template = "xx:xx:xx" return string.gsub(template, "x", function(c) return string.format("%x", random(0, 0xf)) end) end function SystemDb.addSystem(self, systemFunc, params) local system if type(systemFunc) == "table" then system = systemFunc else local params = params or {} params.db = self.entityDb system = systemFunc(params) end local id = systemId() self.systems[id] = system table.insert(self.sorted, id) self:sort() return id end function SystemDb.sort(self) table.sort(self.sorted, function(a, b) return (self.systems[a].priority or 100) < (self.systems[b].priority or 100) end) end function SystemDb.update(self, dt) for _, system in ipairs(self.sorted) do self.systems[system]:update(dt) end end function SystemDb.removeSystem(self, id) self.systems[id] = nil for i=#self.sorted,1,-1 do if self.sorted[i] == id then table.remove(self.sorted, i) end end end --===== Database =====-- Database = {} Database.__index = Database function Database.new(_) local self = {} self.entityDb = EntityDb() self.systemDb = SystemDb(self.entityDb) setmetatable(self, Database) return self end setmetatable(Database, {__call=Database.new}) function Database.isValidEntity(self, id) return self.entityDb:checkIsValid(id) end function Database.createEntity(self, id) return self.entityDb:createEntity(id) end function Database.addComponent(self, id, name, value) return self.entityDb:addComponent(id, name, value) end function Database.queryComponent(self, name) return self.entityDb:queryComponent(name) end function Database.queryEntity(self, id) return self.entityDb:queryEntity(id) end function Database.getComponent(self, id, name) return self.entityDb:getComponent(id, name) end function Database.removeComponent(self, id, name) return self.entityDb:removeComponent(id, name) end function Database.deleteEntity(self, id) return self.entityDb.deleteEntity(id) end function Database.addSystem(self, func, params) return self.systemDb:addSystem(func, params) end function Database.update(self, dt) return self.systemDb:update(dt) end function Database.removeSystem(self, id) return self.systemDb:removeSystem(id) end return module