Binary serialisation for save files using Mapster & MessagePack - a naive attempt
General waffle around the project
So I checked my schedule again and I noticed, that I was one week behind. I still had work to do regarding saving and loading a save file of the game. Not a big deal I thoght to myself, because I had two vacation weeks coming. Which means I can use way more time than usual to work on it and sure enough I finished major parts until Thursday. However, On Saturday I had a flight to Poland and I will be spending that week there. I just spend some time with my parents there and help them with some work. Boy did I underestimate the time it takes to help them with house work. Also, running the project on a laptop here is kinda difficult, when a certain someone left the code in a broken state. So instead of trying to get things working I figured I will just write this little post to keep, whoever may read these dev logs, informed. I hope there will be a day where I can continue with some proper work again, because I still need to finish the entity refactor until last weekend - yikes.
What's new?
The game is able to save the state of the game into a binary save file and load states from a binary file.
Techincal details
Basically to save the game I do 2 steps. First, I serialise relevant classes into an exclusive model class, which is going to be used for step 2. Secondly, I use the serialised data to write bytes in a file. In order to load the game I just reverse the steps. Now, that the system is implemented, I'd say it is fairly robust and while it is not quite nice to scale things for reasons I will explain later, it is clear and structured. By using a binary format, I make it more difficult for players to cheat their way through the game. But I am sure there are plenty of great hackers, who are more than capable to read and modify these files. With that being said, let's dive deeper into the two steps I do for saving and loading the game.
Serialisation
From my work I am used to use AutoMapper. AutoMapper is great for simple mapping work assuming you understand some basics of the framework. But it didn't took long for me to learn, that using AutoMapper actually costs money. OK, let's not use this then. Sooo I use Mapster. I created a bunch of classes which represent a base class of a game structure I would like to save. Such as entities like slimes or animals or so and objects like plants that you will be able to gather. Then you need to make sure to tell Mapster in a configuration method you do at the start of your application or service: "Serialise from A to B and from B to A". It will then proceed to check for common Properties and magically create the (de-)serialised object. Unlike AutoMapper, Mapster requires you to tell each configuration method which configuration to inherit in case you need it. Which makes generic configurations or worse conditional configurations a little more messy. For instance, I think my object classes actually use reflection and a for each loop in order to apply different kinds of configuration if my object has the interface IInventory or not. AutoMapper I think would just understand, that there is a navigation configuration for IInventory interfaces and apply them automatically. Each serialisable class will be stored within one model class with the name SaveFile and that SaveFile class is then being processed in the next step.
Binary save files
So now that I have all my necessary game data neatly stored in primitive or simple properties inside a model class (or POCO class or entity or whatever you want to call it) I can finally save the data in a file. But wait! What I actually need are a bunch of bytes that will be stored in a file. For that I could make something like a toByteArray() method, which will check all the properties of all the subclasses and put them into an array which will then be saved. But I don't need to do all the extra work with *MessagePack* :)
MessagePack essentially allows me to define attributes above properties that I would like to have stored in the file and it will do its normed binary serialisation magic in order to create a sequence of bytes, which can then be deserialized in a modular and structured way (and fast). Actually, assigning attributes to each property is kinda tideous and MessagePack does allow me to also handle this and the order of storage itself. But I have a complex save file (ie. a SaveFile with many generic base-class-representing models, which in turn include concrete data for some properties only in some conditions.). However, they do recommend assigning the attribute if you want to be sure, that there will be no deserialisation issues. My save file model classes are thus a summary of the current game state with information about proper (de-)serialisation stored in attributes. Like an inbetween layer.
Little post-mortem (and explaination of why it took longer than expected)
With Mapster and MessagePack I used two libraries I've never worked before. I didn't really learn much in terms of serialisation or mapping, but it was surely a nice little challenge to work with something I was not familiar with. Also, while each of the processes are fairly easy to understand and use in an isolated manner, working with many systems harmoning all at once was surely challenging. For instance, in my prior week I just finished the scene which the game will navigate to whenever something needs to be loaded in the background. Basically a fancy loading screen. That screen of course does the heavy workload in the background. MessagePack however uses static methods and kinda expects you to run their methods synchroneously. I assume we could still use them without an issue using a proper Task async programming, but my MonoGame framework actually runs synchroneously, so I wouldn't be able to use Tasks all the way up, which means that running these async operations is risky and may not work sometimes. So naturally I made some mistakes when calling IO methods (Loading IO shoud be the first method in the top most async method), also in attribute assignments, configurations and alike. And the worst part of them all, you can only really test the system, once you have a structure to save AND load save files. Only then I was able to realise, that my serialisation had issues, which meant that assumptions during investigations of my load methods were obsolete and so it was a constant back and fourth. BUT! It is done. I am happy that it works. As I wrote earlier, it is robust and has clear structure. The time and work that it took is granted, because save files are just fundamental and will always require maintenance. Unlike ugly animations or minor insignificant glitches. Speaking of which...
What's next?
Up next I will be doing a minor entity refactor, which will change the way creatues/player move around the world. Frankly, ever since the prototype I never touched that code again and I actually broke some things during the development of other features. But that is all fine and planned. After the movement-refactor I will implement controller support, because I am convinced this will bumb up the quality of the game by a decent amount. And having movement set to stone will likely help. Afterwards I will complete the entity refactor (except interaction logic).
Get Slime Breeder
Slime Breeder
Prototype of a slime breeding game
| Status | Released |
| Author | 0l!ver |
| Genre | Simulation |
| Tags | 2D, breed, breeding, Casual, Farming, Prototype, Singleplayer, Slime, slime-breeder |
More posts
- Entities, entities and entities7 hours ago
- Scene transitions & Loading screen27 days ago
- Scenes35 days ago
- Maps42 days ago
- Map Builder62 days ago
- Slime Breeder67 days ago
Leave a comment
Log in with itch.io to leave a comment.