Persistent world with UE4 (not an MMO)

Two separate topics;
Networking replication is one (huge) thing, data persistence on server is a whole another (huge) thing.
Btw, saving every single bit of your game to be a persistent world will kill your pocket, keep in mind hosting services such as Amazon’s and SoffLayer will charge you for both bandwidth traffic and HDD read/write time; ridiculous, I know.

For this type of discussion a Live Skype Call and screenshare would be beneficial. In the interim, visuals can work. I would need to see the Blueprints to get a sense of what data you’re saving/loading and where the problem is. I use SGOs often so I’m confident I could quickly spot the problem. The question is have you been able to successfully Save/Load with simple Variable from a SGO? If the answer is yes, you’re merely expanding on the number of Variables.

Mathew Wadstein’s gives a decent walk through using the Save Game Object.

The process is: The application (UE4 Engine) generates (spawns) the actor (object) instance to hold the data, and the data is loaded from a local/remote source and assigned to that object. This is the process no matter if the application is WinWord and data is stored in .docx format. Winword would have to spawn the Document and load the data from .docx to display it to the user.

Step 3 is what makes working with the SGO somewhat awkward, because it requires extra nodes to transfer data between the working variables and SGO variables. But, just think of t as a SGO’s way to format data with BP variables. You use Variable Getter/Setters to transfer the data between the SGO Var and the Game Var.

I prefer using Arrays of User Define Structs, for me they resemble a Data Table | Database Table. So I work with them similar to Database Tables creating Primary Keys of Array Indices, Names, Object References to associate User Defined Structures to others.

*** Optional Reading ***

The number of User Defined Structs is dependent on differences in data between the Actors. For example, if you’re storing Level Map comprised of different Static Meshes, orientation and scale, then you would only need a single User Defined Type to hold Static Mesh Ref, Location Vector, Rotation Vector, Scale Vector. The Static Mesh Ref Variable could point different Walls, Doors, Furniture, Props, etc, so there is no need for a different User Struct for this info. However, if you needed to associate some logic to the Doors, than you would additional variables to hold that info, which could be encapsulated in another user define struct.

Ah thank you very much, this was the most helpful part of the message here so far :slight_smile: Although I am confused here bit about one thing. When I store extra variables for eg. Doors to a separate struct, how would I actually link it to the actor reference that got created based on Location Vector (and other basic stuff)? I assume I would need some kind of GUID?

Also second part of the previous post about savegame backward compatibility, I am going to rephrase it. Simply put, when I save those extra information about Doors and then later I decide to remove one of those variables. Would be SGO able to load older save game file that still has this variable present?

You could use Integers, Strings, Names, Object References data types for unique IDs. I prefer to use the Name Datatype for IDs for human readability, string compatibility, and performance (as the Engine resolves them to a integer value). In some cases you’ll use integers that correspond with the Array Index that element exists in.

In the Pseudo-code below, the ‘Key’ Variable is used to associate one piece of user-defined struct or class to another:

LevelObject {
    Name Key
    StaticMesh Mesh
    Transform Orientation

DoorLogic {
    Name Key
    Name LevelObjectKey
    Byte Type
    Byte State    

As this info is stored in Arrays, you use Find, Contains BP Nodes to filter on the Arrays.

SGO Backwards compatibility. I personally do not see a case for this. Its more advantageous to figure out what the User define Structure needs before even saving/loading data. The Slot Files produced by SGO are binary, so If you remove a variable from User Defined Structure, you alter the binary format, thus data will not load. So, you’ll have to migrate from the old Structure to a New One, if you want to preserve that data. In migrating, I’d recommend creating new SGOs BPs to load the Old Structure into and Save with the new SGO.

Thanks for pointing out about Name data type, that sounds like a good idea :slight_smile:

Case for backward compatibility is quite simple. You have a single player game where player saves his game. We release a new version of the game which has changed format of the save file. Apparently that player wants continue playing from existing save file even with a new version :slight_smile: I understand problem of binary files and that’s why I am so concerned about it.

Not sure how I would approach about that migration. I mean that SGO is referencing some structure which has changed in a code. Won’t it fail when even trying to load from that slot? I would need to have a different versions of that struct so it always loads and then do a migration to new struct versions.

Essentially the only issue is with removing variables from SGO or changing their order, right? Instead of doing that modification right away, we could exclude it from actual saving in one version and issue warning to players that save game wont be compatible with a next version of the game. It’s certainly the easiest for us, but not very friendly to players.

So I am starting to think that I will need to dive into a C++ with this after all, because backward compatibility of a save file is very important to us. Most probably I would need to create my own binary format for saving instead of SGO to be able recognize which version of that save file it is and run appropriate migration routine.

PS: I do realize this is no longer about multiplayer persistent world and I apologize to readers for side tracking this much.

If you try to load a struct from a FArchive outdated, depending on the changes you’ve made, it won’t just fail to load but it will crash the engine too.

The Upgrade would have to provide migration functions that use the new SGO_v2. The functions would load the old SGO_v1 Slotfiles assigning corresponding variables to SGO_v2, and Save SGO_v2 Slotfiles, overwriting old Slotfiles.

Perhaps you can use one of the available JSON BP Plugins to save data as stings in JSON format. This was the direction I was going in with multiplayer, and I was going to use a migration tool to load the old SGO slotfiles into the new ones that use Json Strings. I have yet to install 3rd party plugins to use JSON was hoping for native BP solution.

Right, the save file actually holds information which SGO was used for making it, am I correct? In that case creating new SGO for breaking change should solve it and then simply try casting to latest version and in case of fail do a migration. Yea that could work :slight_smile:

However I have also another concern about SGO as they do not provide way to actually give me list of available slots. In our case I want to be able to select which save file to load, but I am unable to get that list.

Since I am coming from JavaScript world I am thinking to dive deeply into Unreal.JS project which looks very promising. I would be able to create custom save/load routine there much more easily for sure.

Anyway thanks again for your input, much appreciated. We are getting to a point in our game when I will need to implement this, so all this comes very handy.

You would have to create SGO to manage the list of slots. The need to do this was inspiration for my Visual Slot Management System current on hold, pending completion of EyeManga3D for submission to the Unreal Marketplace.

Well that already looks like bending over something that wasn’t meant to be used like that. I can see SGO as a great system for checkpoint style save system. Even for storing single persistent world, but our case is bit more complicated.

User will be able to download maps from external server. Manually at the beginning, later with some in game routine. It would be impossible to modify SGO based on new file being downloaded. It will be much better to have a blueprint node that is able to return list of maps out of list of files present on file system.

Also we want some sort of metadata with each saved map like name, description and other stuff that can be displayed before trying to load the map.