mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-09-02 12:24:11 -04:00
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.
This commit is contained in:
parent
3d195c790f
commit
a55efd042a
6 changed files with 148 additions and 15 deletions
|
@ -2,4 +2,5 @@ local PATH = (...):gsub("(%.init)$", "")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
serializable = require(PATH..".serializable"),
|
serializable = require(PATH..".serializable"),
|
||||||
|
key = require(PATH..".key"),
|
||||||
}
|
}
|
||||||
|
|
37
concord/builtins/key.lua
Normal file
37
concord/builtins/key.lua
Normal file
|
@ -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
|
|
@ -74,6 +74,7 @@ function Component:deserialize(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Internal: Creates a new Component.
|
-- Internal: Creates a new Component.
|
||||||
|
-- @param entity The Entity that will receive this Component.
|
||||||
-- @return A new Component
|
-- @return A new Component
|
||||||
function Component:__new(entity)
|
function Component:__new(entity)
|
||||||
local component = setmetatable({
|
local component = setmetatable({
|
||||||
|
@ -88,6 +89,7 @@ function Component:__new(entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Internal: Creates and populates a new Component.
|
-- Internal: Creates and populates a new Component.
|
||||||
|
-- @param entity The Entity that will receive this Component.
|
||||||
-- @param ... Varargs passed to the populate function
|
-- @param ... Varargs passed to the populate function
|
||||||
-- @return A new populated Component
|
-- @return A new populated Component
|
||||||
function Component:__initialize(entity, ...)
|
function Component:__initialize(entity, ...)
|
||||||
|
|
|
@ -9,7 +9,8 @@ local Type = require(PATH..".type")
|
||||||
local Utils = require(PATH..".utils")
|
local Utils = require(PATH..".utils")
|
||||||
|
|
||||||
-- Initialize built-in Components (as soon as possible)
|
-- 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 = {
|
local Entity = {
|
||||||
SERIALIZE_BY_DEFAULT = true,
|
SERIALIZE_BY_DEFAULT = true,
|
||||||
|
@ -49,7 +50,7 @@ local function give(e, name, componentClass, ...)
|
||||||
local hadComponent = not not e[name]
|
local hadComponent = not not e[name]
|
||||||
|
|
||||||
if hadComponent then
|
if hadComponent then
|
||||||
e[name]:removed()
|
e[name]:removed(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
e[name] = component
|
e[name] = component
|
||||||
|
@ -61,7 +62,7 @@ end
|
||||||
|
|
||||||
local function remove(e, name)
|
local function remove(e, name)
|
||||||
if e[name] then
|
if e[name] then
|
||||||
e[name]:removed()
|
e[name]:removed(false)
|
||||||
|
|
||||||
e[name] = nil
|
e[name] = nil
|
||||||
|
|
||||||
|
@ -207,11 +208,16 @@ function Entity:getWorld()
|
||||||
return self.__world
|
return self.__world
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:serialize()
|
function Entity:serialize(ignoreKey)
|
||||||
local data = {}
|
local data = {}
|
||||||
|
|
||||||
for name, component in pairs(self) do
|
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()
|
local componentData = component:serialize()
|
||||||
|
|
||||||
if componentData ~= nil then
|
if componentData ~= nil then
|
||||||
|
@ -234,7 +240,7 @@ function Entity:deserialize(data)
|
||||||
|
|
||||||
local componentClass = Components[componentData.__name]
|
local componentClass = Components[componentData.__name]
|
||||||
|
|
||||||
local component = componentClass:__new()
|
local component = componentClass:__new(self)
|
||||||
component:deserialize(componentData)
|
component:deserialize(componentData)
|
||||||
|
|
||||||
self[componentData.__name] = component
|
self[componentData.__name] = component
|
||||||
|
|
|
@ -48,6 +48,8 @@ System.mt = {
|
||||||
-- @param table filters A table containing filters (name = {components...})
|
-- @param table filters A table containing filters (name = {components...})
|
||||||
-- @treturn System A new SystemClass
|
-- @treturn System A new SystemClass
|
||||||
function System.new(definition)
|
function System.new(definition)
|
||||||
|
definition = definition or {}
|
||||||
|
|
||||||
for name, def in pairs(definition) do
|
for name, def in pairs(definition) do
|
||||||
if type(name) ~= 'string' then
|
if type(name) ~= 'string' then
|
||||||
Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name))
|
Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name))
|
||||||
|
|
|
@ -19,6 +19,12 @@ World.__mt = {
|
||||||
__index = World,
|
__index = World,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local defaultGenerator = function (state)
|
||||||
|
local current = state
|
||||||
|
state = state +1
|
||||||
|
return string.format("%d", current), state
|
||||||
|
end
|
||||||
|
|
||||||
--- Creates a new World.
|
--- Creates a new World.
|
||||||
-- @treturn World The new World
|
-- @treturn World The new World
|
||||||
function World.new()
|
function World.new()
|
||||||
|
@ -29,6 +35,13 @@ function World.new()
|
||||||
__events = {},
|
__events = {},
|
||||||
__emitSDepth = 0,
|
__emitSDepth = 0,
|
||||||
|
|
||||||
|
__hash = {
|
||||||
|
state = -2^53,
|
||||||
|
generator = defaultGenerator,
|
||||||
|
keys = {},
|
||||||
|
entities = {}
|
||||||
|
},
|
||||||
|
|
||||||
__added = List(), __backAdded = List(),
|
__added = List(), __backAdded = List(),
|
||||||
__removed = List(), __backRemoved = List(),
|
__removed = List(), __backRemoved = List(),
|
||||||
__dirty = List(), __backDirty = 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))
|
Utils.error(2, "bad argument #1 to 'World:removeEntity' (Entity expected, got %s)", type(e))
|
||||||
end
|
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)
|
self.__removed:add(e)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -343,16 +364,16 @@ function World:getSystems()
|
||||||
return self.__systems
|
return self.__systems
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:serialize()
|
function World:serialize(ignoreKeys)
|
||||||
self:__flush()
|
self:__flush()
|
||||||
|
|
||||||
local data = {}
|
local data = { generator = self.__hash.state }
|
||||||
|
|
||||||
for i = 1, self.__entities.size do
|
for i = 1, self.__entities.size do
|
||||||
local entity = self.__entities[i]
|
local entity = self.__entities[i]
|
||||||
|
|
||||||
if entity.serializable then
|
if entity.serializable then
|
||||||
local entityData = entity:serialize()
|
local entityData = entity:serialize(ignoreKeys)
|
||||||
table.insert(data, entityData)
|
table.insert(data, entityData)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -360,21 +381,85 @@ function World:serialize()
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:deserialize(data, append)
|
function World:deserialize(data, startClean, ignoreGenerator)
|
||||||
if (not append) then
|
if startClean then
|
||||||
self:clear()
|
self:clear()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if (not ignoreGenerator) then
|
||||||
|
self.__hash.state = data.generator
|
||||||
|
end
|
||||||
|
|
||||||
|
local entities = {}
|
||||||
|
|
||||||
for i = 1, #data do
|
for i = 1, #data do
|
||||||
local entityData = data[i]
|
local entity = Entity(self)
|
||||||
|
|
||||||
local entity = Entity()
|
if data[i].key then
|
||||||
entity:deserialize(entityData)
|
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
|
end
|
||||||
|
|
||||||
self:__flush()
|
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
|
end
|
||||||
|
|
||||||
--- Callback for when an Entity is added to the World.
|
--- Callback for when an Entity is added to the World.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue