mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-09-01 11:53:57 -04:00
Add serialization and deserialization functions to component, entity, world
This commit is contained in:
parent
c217183cb9
commit
6cd66e6737
6 changed files with 214 additions and 33 deletions
|
@ -18,6 +18,7 @@ function Component.new(populate)
|
||||||
local componentClass = setmetatable({
|
local componentClass = setmetatable({
|
||||||
__populate = populate,
|
__populate = populate,
|
||||||
|
|
||||||
|
__name = nil,
|
||||||
__isComponentClass = true,
|
__isComponentClass = true,
|
||||||
}, Component.__mt)
|
}, Component.__mt)
|
||||||
|
|
||||||
|
@ -32,22 +33,44 @@ end
|
||||||
function Component:__populate() -- luacheck: ignore
|
function Component:__populate() -- luacheck: ignore
|
||||||
end
|
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.
|
--- Internal: Creates and populates a new 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(...)
|
function Component:__initialize(...)
|
||||||
local component = setmetatable({
|
local component = self:__new()
|
||||||
__componentClass = self,
|
|
||||||
|
|
||||||
__isComponent = true,
|
|
||||||
__isComponentClass = false,
|
|
||||||
}, self)
|
|
||||||
|
|
||||||
self.__populate(component, ...)
|
self.__populate(component, ...)
|
||||||
|
|
||||||
return component
|
return component
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Component:hasName()
|
||||||
|
return self.__name and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Component:getName()
|
||||||
|
return self.__name
|
||||||
|
end
|
||||||
|
|
||||||
return setmetatable(Component, {
|
return setmetatable(Component, {
|
||||||
__call = function(_, ...)
|
__call = function(_, ...)
|
||||||
return Component.new(...)
|
return Component.new(...)
|
||||||
|
|
|
@ -20,12 +20,22 @@ function Components.register(name, componentClass)
|
||||||
end
|
end
|
||||||
|
|
||||||
if (rawget(Components, name)) then
|
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
|
end
|
||||||
|
|
||||||
Components[name] = componentClass
|
Components[name] = componentClass
|
||||||
|
componentClass.__name = name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Components.has(name)
|
||||||
|
return Components[name] and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Components.get(name)
|
||||||
|
return Components[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return setmetatable(Components, {
|
return setmetatable(Components, {
|
||||||
__index = function(_, name)
|
__index = function(_, name)
|
||||||
error("Attempt to index ComponentClass '"..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)
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
local PATH = (...):gsub('%.[^%.]+$', '')
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
||||||
|
|
||||||
local Type = require(PATH..".type")
|
local Components = require(PATH..".components")
|
||||||
|
local Type = require(PATH..".type")
|
||||||
|
|
||||||
local Entity = {}
|
local Entity = {}
|
||||||
Entity.__mt = {
|
Entity.__mt = {
|
||||||
|
@ -173,6 +174,41 @@ function Entity:getWorld()
|
||||||
return self.__world
|
return self.__world
|
||||||
end
|
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, {
|
return setmetatable(Entity, {
|
||||||
__call = function(_, ...)
|
__call = function(_, ...)
|
||||||
return Entity.new(...)
|
return Entity.new(...)
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
local PATH = (...):gsub('%.[^%.]+$', '')
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
||||||
|
|
||||||
local Type = require(PATH..".type")
|
local Entity = require(PATH..".entity")
|
||||||
local List = require(PATH..".list")
|
local Type = require(PATH..".type")
|
||||||
local Utils = require(PATH..".utils")
|
local List = require(PATH..".list")
|
||||||
|
local Utils = require(PATH..".utils")
|
||||||
|
|
||||||
local World = {
|
local World = {
|
||||||
ENABLE_OPTIMIZATION = true,
|
ENABLE_OPTIMIZATION = true,
|
||||||
|
@ -21,10 +22,10 @@ World.__mt = {
|
||||||
-- @return The new World
|
-- @return The new World
|
||||||
function World.new()
|
function World.new()
|
||||||
local world = setmetatable({
|
local world = setmetatable({
|
||||||
entities = List(),
|
__entities = List(),
|
||||||
systems = List(),
|
__systems = List(),
|
||||||
|
|
||||||
events = {},
|
__events = {},
|
||||||
|
|
||||||
__added = List(), __backAdded = List(),
|
__added = List(), __backAdded = List(),
|
||||||
__removed = List(), __backRemoved = List(),
|
__removed = List(), __backRemoved = List(),
|
||||||
|
@ -104,10 +105,10 @@ function World:__flush()
|
||||||
for i = 1, self.__backAdded.size do
|
for i = 1, self.__backAdded.size do
|
||||||
e = self.__backAdded[i]
|
e = self.__backAdded[i]
|
||||||
|
|
||||||
self.entities:__add(e)
|
self.__entities:__add(e)
|
||||||
|
|
||||||
for j = 1, self.systems.size do
|
for j = 1, self.__systems.size do
|
||||||
self.systems[j]:__evaluate(e)
|
self.__systems[j]:__evaluate(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:onEntityAdded(e)
|
self:onEntityAdded(e)
|
||||||
|
@ -119,10 +120,10 @@ function World:__flush()
|
||||||
e = self.__backRemoved[i]
|
e = self.__backRemoved[i]
|
||||||
|
|
||||||
e.__world = nil
|
e.__world = nil
|
||||||
self.entities:__remove(e)
|
self.__entities:__remove(e)
|
||||||
|
|
||||||
for j = 1, self.systems.size do
|
for j = 1, self.__systems.size do
|
||||||
self.systems[j]:__remove(e)
|
self.__systems[j]:__remove(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:onEntityRemoved(e)
|
self:onEntityRemoved(e)
|
||||||
|
@ -133,8 +134,8 @@ function World:__flush()
|
||||||
for i = 1, self.__backDirty.size do
|
for i = 1, self.__backDirty.size do
|
||||||
e = self.__backDirty[i]
|
e = self.__backDirty[i]
|
||||||
|
|
||||||
for j = 1, self.systems.size do
|
for j = 1, self.__systems.size do
|
||||||
self.systems[j]:__evaluate(e)
|
self.__systems[j]:__evaluate(e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.__backDirty:__clear()
|
self.__backDirty:__clear()
|
||||||
|
@ -168,18 +169,18 @@ function World:addSystem(systemClass)
|
||||||
local system = systemClass(self)
|
local system = systemClass(self)
|
||||||
|
|
||||||
self.__systemLookup[systemClass] = system
|
self.__systemLookup[systemClass] = system
|
||||||
self.systems:__add(system)
|
self.__systems:__add(system)
|
||||||
|
|
||||||
for callbackName, callback in pairs(systemClass) do
|
for callbackName, callback in pairs(systemClass) do
|
||||||
-- Skip callback if its blacklisted
|
-- Skip callback if its blacklisted
|
||||||
if (not blacklistedSystemFunctions[callbackName]) then
|
if (not blacklistedSystemFunctions[callbackName]) then
|
||||||
-- Make container for all listeners of the callback if it does not exist yet
|
-- Make container for all listeners of the callback if it does not exist yet
|
||||||
if (not self.events[callbackName]) then
|
if (not self.__events[callbackName]) then
|
||||||
self.events[callbackName] = {}
|
self.__events[callbackName] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add callback to listeners
|
-- Add callback to listeners
|
||||||
local listeners = self.events[callbackName]
|
local listeners = self.__events[callbackName]
|
||||||
listeners[#listeners + 1] = {
|
listeners[#listeners + 1] = {
|
||||||
system = system,
|
system = system,
|
||||||
callback = callback,
|
callback = callback,
|
||||||
|
@ -188,8 +189,8 @@ function World:addSystem(systemClass)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Evaluate all existing entities
|
-- Evaluate all existing entities
|
||||||
for j = 1, self.entities.size do
|
for j = 1, self.__entities.size do
|
||||||
system:__evaluate(self.entities[j])
|
system:__evaluate(self.__entities[j])
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -243,7 +244,7 @@ function World:emit(functionName, ...)
|
||||||
error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")")
|
error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")")
|
||||||
end
|
end
|
||||||
|
|
||||||
local listeners = self.events[functionName]
|
local listeners = self.__events[functionName]
|
||||||
|
|
||||||
if listeners then
|
if listeners then
|
||||||
for i = 1, #listeners do
|
for i = 1, #listeners do
|
||||||
|
@ -263,17 +264,58 @@ end
|
||||||
--- Removes all entities from the World
|
--- Removes all entities from the World
|
||||||
-- @return self
|
-- @return self
|
||||||
function World:clear()
|
function World:clear()
|
||||||
for i = 1, self.entities.size do
|
for i = 1, self.__entities.size do
|
||||||
self:removeEntity(self.entities[i])
|
self:removeEntity(self.__entities[i])
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, self.systems.size do
|
for i = 1, self.__systems.size do
|
||||||
self.systems[i]:__clear()
|
self.__systems[i]:__clear()
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
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.
|
--- Callback for when an Entity is added to the World.
|
||||||
-- @param e The Entity that was added
|
-- @param e The Entity that was added
|
||||||
function World:onEntityAdded(e) -- luacheck: ignore
|
function World:onEntityAdded(e) -- luacheck: ignore
|
||||||
|
|
69
examples/serialization/init.lua
Normal file
69
examples/serialization/init.lua
Normal file
|
@ -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)
|
1
main.lua
Normal file
1
main.lua
Normal file
|
@ -0,0 +1 @@
|
||||||
|
require("examples.serialization")
|
Loading…
Add table
Add a link
Reference in a new issue