Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
[help] complete Object Serialization? — Gideros Forum

[help] complete Object Serialization?

HolonistHolonist Member
edited February 2015 in General questions
Hi fellow Gideros users,

I know this has been asked (and answered as impossible before), but does anyone know how to serialize objects in Gideros?
Please read on before you give me the "repost-hammer".

I was working on a little "auto-battle rpg" game in LÖVE2D recently (I wasn't aware there is already such a genre, so it's a little disappointing I'm not the first one to do this).

However, in my project I have fairly complex classes/objects I need to be able to save. I give you a little pseudo-code to show you what I mean

class "Equippable":
- stat1
- stat2
- stat3
- func:changesomething()

class "Inventory":
- self={}
- func self:add(Equippable)

class "Player":
- stat1
- stat2
- stat3
- inventory = Inventory:new()
- self.equipment = {
Equippable:new()
Equippable:new()
Equippable:new()
}
- func:updateStats()
- func:xyz()

In reality the objects are even more complex and even Equippable has sub-objects called "manastones", which also have their own attributes and functions.

In LÖVE2D, i use a combination of "MiddleClass.lua" and "Lady.lua" (i think both are LÖVE specific) to save and load my player. For saving and loading the complete player object, I only have to write one line of code:
- lady_save_all("player.txt", player)
- player = lady_load_all("player.txt")

What lady does:
- it saves the object table recursively, (without any functions!)
- it adds the class name to the saved data
- it magically restores objects, no matter how complex, with completely functioning subobjects.

Because I want to port my game to Gideros, I would need something similar here. I tried JSON.encode, which is MOSTLY fine, but I lose my functions.

I tried to store all attributes in object.data (array), and when i load the game, initialize player and load player.data. Problem with this is, it doesn't work if my player contains subobjects like Equippable:new(), as those will be ignored by JSON.encode.

Any ideas, solutions? Should I try to use MiddleClass instead of Gideros class system and try to get Lady to work?


--
I will come back to edit the formatting of this post, unfortunately I have very little time atm.

Greetings to all!


Edit: One solution (even though it's not 'real' serialization) is attached in the zip below. It's a demo Gideros project that uses Lady to save an object, it's subojects and their functions (which aren't saved in the file, but correctly restored in runtime).

If you want to use it, you only have to copy MiddleClass.lua and Lady.lua, and the codeblock in my init.lua file. The rest is just the make the demo work.

Comments

  • ar2rsawseenar2rsawseen Maintainer
    edited February 2015
    Why it is not possible to serialize lua objects is that they might have C pointers in them.

    But it should be possible to traverse the objects and collect all other data, create table with that data and serialize it.
    Something like this could work

    Maybe need to adjust more specific to some types (http://www.lua.org/pil/2.html)
    function serialize(object, data)
        data = data or {}
        for i in pairs(object) do
            if type(object[i]) ~= "nil" and type(object[i]) ~= "thread" and type(object[i]) ~= "function"  and type(object[i]) ~= "userdata" then
                if type(object[i]) == "table" then
                    data[i] = serialize(object[i], data)
                else
                    data[i] = object[i]
                end
            end
        end
        return data
    end
    and then use either dataSaver module or save/load table

    http://giderosmobile.com/forum/discussion/367/save-load-a-table/p1
  • Sorry, I don't really get your answer.
    But surprisingly, porting Lady to Gideros was easier than I thought. I basically just had to replace the love-specific save mechanism

    love.filesystem.write("file.txt", content)

    to

    file = io.open("file.txt", "w")
    file:write(content)
    file:close()

    (same for loading.)

    The magic that happens before saving/loading was apparently pure lua and is therefore usable in any framework.

    With that obstacle out of the way, I think I'm going to be able to develop my game in Gideros. If anyone's interested, I can post the modified Lady.lua and instructions how to use it.
  • @holonist It will be a great contribution . =D>
  • I added a zip file with a prepared gideros project.
    Please try it and let me know what you think.

    Likes: pie

    +1 -1 (+1 / -0 )Share on Facebook
  • @Holonist thank you it's really cool! :) not just the lady port but the whole project:
    I was wondering where to start to learn a proper use of classes (actually I use them as a monkey can use a calculator :(|) ) , and I think that now I do.. :)

    I have a question about "middleclass.lua": what does it do exactly? What are the differences/advantages from creating new classes as in
    testClass = Core.class()
    function testClass:init() 
    ...blah blah.. 
    end
    ?

    Thank you
  • HolonistHolonist Member
    edited February 2015
    I don't know if there are any advantages.

    It is necessary for Lady to work, so make sure you use middleclass for every object you want to serialize with Lady. (you can make it work with other class systems, but I don't know how to be honest).

    For everything that doesn't have to be saved accurately, like buttons, sprites etc, I would recommend to use the Gideros class system, because otherwise you can't inherit easily from Sprite, EventDispatcher, etc.

    Fortunately, you can use both class mechanisms in the same project (at least i didn't find incompatibility yet).

    Edit:
    One tip, if you want to use inheritance with middleclass, you do it like this.
    Person = class('Person')
     
    function Person:initialize(name, age)
    	self.name = name
    	self.age = age
    end
     
    function Person:getOlder()
    	self.age = self.age+1
    end
     
     
    BusinessMan = class('BusinessMan', Person) --important to get this line right
     
    function BusinessMan:initialize(name, age, hobby)
    	Person.initialize(self, name, age) --important to get this line right too
    	self.hobby = hobby
    end
     
    function BusinessMan:sayHello()
    	print("Hello, I'm a businessman, my hobby is " .. self.hobby)
    end
    BusinessMan will have access to the same fields and functions that Person has.

    In Gideros it functions similarly, but you can find that in the forums.
    (Also, you will find additional info to Middleclass on their github page)
  • Another thing. If you want to produce, let's say 100 Businessmen.

    You can do it like this:
    for i=1, 100 do
    	local a = BusinessMan:new("somename", i, "somehobby")
    end
    but please, don't because you will have no way to access those objects later.

    do it like this:
    businessMen = {}
     
    for i=1, 100 do
    	businessMen[i] = BusinessMan:new("somename", i, "somehobby")
    end
    This way you can track/manipulate your objects later on.
    Not getting this right gave me huge problems for a long time, therefore i mention it. Otherwise sorry for messing up the thread :P

    Likes: Jacko

    +1 -1 (+1 / -0 )Share on Facebook
  • Thank you,
    about the last tip: good point writing it down.
    I already figured that out some time ago, but I wish I found it somewhere before wasting all that time.. :)) - it is the same using gideros Class system.

    businessMen = {}
    could also be local if your tracking needs are in the same scope
Sign In or Register to comment.