The basic idea behind my serialization approach is, to assign an unique id to each object (=lua table) and use this unique id to save associations between objects. To archieve this, I've started to assign an unique id to each object at creation time:
-- global data
uid_counter = 0
uid_map = {}
createNewObject = function()
uid_counter = uid_counter + 1
local object = {}
object.uid = uid_counter
uid_map[uid_counter] = object
return object
endThen ,when you write your data to a file, exchange any references to tables by its uid, mark each uid with a special char ,i.e. '#', to distinquish it from other variables.
serializeObject = function( _obj)
write("new_object={")
for key,value in pairs(_obj) do
-- is this a table ?
if type(value)=="table" then
-- is this a object ?
if value.uid~=nil then
-- save reference only
write(" "..key.."= [[#"..value.uid.."]],")
else
-- just a table, write values as table, ignore nested tables/objects
..
else
-- just normal attribute, save it according to it type
if type(value)=="string" then
write(" "..key.."= \""..value.."\",")
...
end
end
-- close object
write("}")
end
We only create objects and hold them in a map which prevents garbage collection, either delete them directly(bad) or clean them up (i.e. just before saving game). Ensure that you really have a complete object net, that is, you need some kind of root object.
cleanup = function()
local is_referenced = {}
for uid,object in pairs(uid_map) do
for key,value in pairs(object) do
if type(value)=="table" and value.uid~=nil then
is_referenced[value.uid] == true
end
end
end
-- remove all objects, which are no longer references and is not the root object
for uid,object in pairs(uid_map) do
if root_uid~=uid and is_referenced[value.uid]==nil then
-- remove
uid_map[uid] = nil
end
end
end
Now we want to serialize the whole object data structure
serializeAll = function()
-- clean up first
cleanup()
-- serialize them now
for uid,object in pairs(uid_map) do
serializeObject(object)
end
end
You now should have a file like this one:
new_object =
{
uid = 23,
test = "Hello World",
attr = 10,
}
new_object =
{
uid = 25,
my_ref = [[#23]],
other_attr = "2928",
}
To deserizialize it (aka loading), just write a new_object method and execute the doFile lua function.
new_object = function(_input)
-- save it to uid map
uid_map[_input.uid] = _input
end
-- once you have read it, you need to reconnect the data
postprocess = function()
local max_uid = 0
for uid,object in pairs(uid_map) do
for key,value in pairs(object) do
if type(value)=="string" and string.find(value,"#")~=nil then
-- extract uid (home work)
local ref_uid = ...
--reconnect with table
object[key]=uid_map[ref_uid]
end
end
max_uid = math.max(uid,max_uid)
end
-- init counter
uid_counter = max_uid
end
That is the basic idea, it is really simple, but need some work:
1. You can compress uids to keep them in a certain range.
2. You can use fixed uid ranges which represent meta-data (i.e. combat tables etc.) and will not be serialized (though the will be automatically reconnected).
3. You can expand this to use C/C++ engine objects (just assign uids to these objects too and write a seraziation mechanism to write them back too).
4. Add some error checking.
Edited by Ashaman73, 14 June 2012 - 01:06 AM.