The Lost Gamestate…

Persisting state of things in an adventure is a really hard thing to crack. Is it coincidence that persisting and resisting sound the same?

I waited some time to implement this because I needed to find out first how the bridging between the Objective C and the Lua code sorted out. Maybe I needed to store the state in anoyher way? And I didn’t had any experience with a dynamic language like Lua. But as I gained more experience with Lua I thought I was ready to implement the save games routines.

But still… The choices you have to make are pretty hard because design mistakes like this will hunt you throughout the development of the game. I looked at various adventure projects currently in development and tried to find out how they had solved this or how they will solve this problem. Some of them solved this by serializing the state if the objects as they were loaded in memory. This is very commonly used in managed languages as Java and C#. In fact,every serious programming language has it’s own way of storing this. Objective C uses NSCoding to make the objects persist-able. I used this technique on earlier games I made but I hated the hard labour you had to put into it. You have to explicitly tell the system what to serialize and not to serialize. And with a complex hierarchy like adventure objects it can become a mess. So I tried to avoid this entirely.

I started this project with Objective C for the graphics and heavy lifting. Lua (a great embedded language)  is used to write the behaviour of my game and manages all the state present in the adventure.

objective-cfree-vector-lua_081531_lua

My first thought was storing each Lua call like versioning systems do. Every time something happens the Lua call will be stored and when the game needs reloading all Lua calls will be executed in the order they were executed before. The big problem with this is that the entire history of calls are getting longer and it can hurt performance in the long run.

The savegames in this scenario will mark several branching points.

This could be a viable solution but besides the pros there are a lot of cons

Pros:

  • Only one big Lua State to manage
  • Very accurate tracking of every possible state in the game
  • Not much coding to do except loading and replaying

Cons:

  • Increasing loading and execution time before starting room
  • Need to store the lua call immediately in memory or database
  • Stores stuff that might not be necessary to be stored.

Most of the solutions I encountered were storing the state of a subset of the Lua objects at specific intervals. This seemed a better approach to me as I had already chosen to make one Lua script per room. Lua fetches all object states when the room is entered and if it’s not present a template will be set.

Right now I can differentiate between 4 kinds of state objects:

  1. RoomState : Stores all the stuff that happens in the room the player is in.
  2. PlayerState : The player (Playable Character) moves between rooms and will be loaded beforehand (to see which room he is and will be his starting point).
  3. NpcState : Some Npc’s (Non Playable Characters) will and can move across different rooms, and they will be added and loaded according their currentroom property.
  4. GlobalState : All event data that needs to be accessible at any time. This will for example be events setting a state variabele that are triggered while having a dialog with a npc and some other dialog depends on that state

The next part of the solution is in which format I can store the lua state. That decision was easy: Json is superefficient because the way lua tables are initialized is json without quotes. I found a very basic json implementation that worked without much fuss. Look at the difference between Lua table creation and it’s Json format :

local room = {
    name="museum",
    objectElevatorDoor={
        standingLocation="{20,2}",
        buttonlocation="{21,3}"
    }
}
{
   "name":"museum",
   "objectElevatorDoor":{
      "standingLocation":"{20,2}",
      "buttonlocation":"{21,3}"
   }
}

It’s basically replacing the equal sign for an semicolon sign and wrap the props in quotes and you have the Json format basically.

jsonlogo

I used this Basic Json parser that parses json to tables and tables to json. The only problem I had was that the parser tried to serialize the functions too.  This could lead to crashes because I could not load the function back from the database.

I wondered if I could seperate the data from the functions. And yes, this can be done really nicely with the use of Lua metatables. A metatable can be seen as an extension point when set on a table. A very good tutorial on how this works can be found here. The structure I came up looks like this:

function common.EntityFactory.createRoom(roomName)
    local room={};
    room = {
        name=roomName
    }
    return room;
end

function common.makePersistable(tableToSetMetatableOn)
    setmetatable(tableToSetMetatableOn,
        {
            __index = function(table, key)
                if key == "persist" then
                    return function()
                        jsonToPrint = json.encode(table);
                        print("LUA JSON FOR room : " .. jsonToPrint);
                        luaController.persist_room(table.name,jsonToPrint);
                    end
                end
            end
        }
    )
end

The showed function is the ability for the object to persist itself.  The luaController is a user data object that calls the Objective C method to persist the Json string.

This defined metatable with the persist function can be set on the player at any time. That way I could create some ObjectOriented approach to handle these kind of things.

common.makePersistable(currentRoom);

The last thing I needed to figure out was how to store this. I chose SSLite because this is a well supported choice on different platforms (android needs to be supported too). I made a nice wrapper for crud operations.

SQLite370.svg

When a save-game is created by the player the intermediate and temporary json records are copied to a different table. It will get an additional timestamp, title and a save image that consist of a freeze frame screenshot. Well this is the plan, that part is not implemented yet. But I have to start somewhere don’t I?

Archived: Uncategorised