• Register

Office Management 101 is a satirical office life simulation tycoon game set in a fictional capitalistic dystopia. Step over competitors, drive your staff to the limit and milk your customers for every penny in the pursuit of spiraling success!

Report RSS Implementing a savegame system

An overview how saving and loading system was implemented in Office Management 101.

Posted by on

Hey!

I promised to describe how I solved saving and loading in Office Management 101, so here goes.

I use the libGDX java library for the game development, since it's a nice framework giving the option to compile the game for both PC and mobile platforms without too much additional trouble, even providing a standardized way to access the file system across all those systems. That already makes a good base to build a saving system on. When I started looking into it I discovered that Nathan Sweet, the guy behind libGDX, also has made a serialization framework Kryo which seemed to make the whole process of converting game elements into savable data even easier, plus even save on speed and size of the data.

Now by no means am I an experienced Java developer and because of that this shouldn't be taken as a tutorial. My professional background is in the web industry and everything I know about Java I've had to learn on my own. This could mean that some of the things I struggle with may seem easy for some and some things I've done could be down right stupid. If any of you notice anything in the following that could be improved, I'd be very thankful if you pointed it out. Java also gets a lot of hate for supposedly being bloated and slow. That may well be, but the well written documentation of libGDX and it's active community is what pulled me over in addition to the obvious reason of supporting several platforms easily. And I'm fairly certain that with proper optimization speed won't be an issue with such as old school looking game as Office Management 101 is. I'm also sure I'll get better and learn as I gain experience working on this game.

Anyway, I've had separated every element of the game into objects since the beginning, as is customary in object-oriented programming anyway. So for example each employee you hire is run by a Character class with the employee's name, statistics, position in the game world, textures and a number of other variables as it's parameters, which may in turn be objects of their own.

To be able to restore a game state, all those objects and their relationships would have to be reconstructed from the saved data. The Kryo library I mentioned easily takes care of the most basic objects out of the box, but many of Office Management 101 object classes were a bit more complex than that. Besides, not all information needs to be saved. Let's take the graphics for example - those are already loaded from separate image files, so saving them again with the other game variables would be a waste of space. Instead a reference which graphics are being used where should be saved.

Kryo provides read and write methods that extend your classes, so that way you can make your own code that converts the object to a serialized data and back, giving you control over which parts of the object get saved and in what format. The saving part itself turned out to be fairly easy, but the problems arose when I tried to reconstruct some of the more complex parts from the savegame. First of all, the things need to be loaded in certain order. The players company should be loaded before his employees for example. But in some cases objects also cross-reference each other, which means they'd have to be constructed partially first and updated later down the loading process once all the required information is available. That caused me to restructure some of the code, which took a lot more time than I had expected when I started. It also took some took quite a bit of concentration to the details, to make sure that everything is loaded in the correct order and not to mix up any similar variables.

What's nice about all this, is that it also solves the problem on Android, when the user tabs out and comes back later. It's a common problem to lose some if not all data stored in the memory during that break. So I made it so that the moment the game gets paused, it creates an autosave that is restored once the game is opened again.


Since maybe some of you would like a bit of a more detailed look into it, I'll demonstrate some simplified sample code as well.

So first of all I create my Kryo object:

Kryo kryo = new Kryo();

Then register my classes, so when they get saved, Kryo doesn't need to additionally save the class name, also providing an ID to the class to identify it:

kryo.register(Room.class, 109);
kryo.register(GameCharacter.class, 110);

Then I have a save method that creates the file (missing overwriting checks at the moment):

FileHandle file = Gdx.files.local(fileName + fileExtension);
Output output = new Output(file.write(false));

It then writes some basic information like the game name, version and date:

output.writeString(Version.name);
output.writeInt(Version.versionCode, true);
kryo.writeObject(output, new Date());

Write the main object that contains all the others:

kryo.writeObjectOrNull(output, kryoObject, kryoObject.getClass());


And close the file:

output.close();

The load method tries to open the file:

Input input = null;
try {
    FileHandle file = Gdx.files.local(fileName + fileExtension);
    input = new Input(file.read());
} catch (Exception e) {
    // Doing some error handling here
}

If everything is successful try to read some initial stuff (is also wrapped in a try-catch in case errors occur with file reading):

String nameString = input.readString();
if (!nameString.equals(Version.name)) {
    input.close();
    // Didn't get the game name from the file? Probably not a correct file, handle the error somehow
}

int versionInt = input.readInt(true);
if (versionInt != Version.versionCode) {
    input.close();
    // Wrong version? Can't load it. More about this at the end of the article
}

// Read the time the game was saved
Date date = kryo.readObjectOrNull(input, Date.class);

// Read the main object
gameObject = kryo.readObjectOrNull(input, className);

// And close the file
input.close();

There's also more error handling there in case objects couldn't be read or miss data (like equal null).

As I mentioned above, some classes have their own Kryo read and write methods. For example the write method for Room class looks something like this:

output.writeInt(this.x1);
output.writeInt(this.y1);
output.writeInt(this.x2);
output.writeInt(this.y2);
output.writeBoolean(this.isOccupied);
output.writeBoolean(this.hasExitPath);
output.writeString(this.parameters.id);
kryo.writeObjectOrNull(output, this.furniture, this.furniture.getClass());


And the read method for the same class:

this.x1 = input.readInt();
this.y1 = input.readInt();
this.x2 = input.readInt();
this.y2 = input.readInt();
this.isOccupied = input.readBoolean();
this.hasExitPath = input.readBoolean();
// This constructs the parameters object based on the saved ID. Parameters holds the variables common to all rooms of this type
this.parameters = new RoomBase(input.readString());
 
this.furniture = kryo.readObjectOrNull(input, Furniture.class);
if (this.furniture == null) {
    this.furniture = new Furniture();
}

In addition there's another method that sets up the graphics for the room based on the type of the room.


Well that's about it. There's plenty to still improve. Making sure corrupted games don't cause any unexpected behaviour for example. Also I need to implement a system that makes it possible to load games saved with an older version of the game. I'm guessing I have to add the appropriate checks for version in each objects Kryo read method and make the code act on that information, constructing the new object variables that weren't in the previous versions with default or randomized values.

Anyhow, feel free to suggest any improvement in my approach to both implementation in the game and the description I've given here. Should the articles be more code or theory oriented? Should I use simpler terms or hardcore programming lingo? What subjects would you like to read about?

You can reach me via twitter, e-mail (riho@tulevik.eu) or leave a comment here.

And thanks for taking your time to check this out.

Seeya,
Riho


@tulevik.EU | officemanagement101.com | DevLog on TIGForums

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: