Complex Save System

Blueprint Only Project
I need a complex save system, where a user can save many things, and I need saves separated into parts so that I don’t load stuff into memory when its not needed.

A large part of my problem, is that using SaveGame, I cannot specify a folder for the “slot”. Which would be nice if possible, because then I could load saves from a directory.

I tried using Asset Manager/Registry but could not get it to return SaveGame assets (perhaps they don’t qualify as assets? which would be nice because I could use Get Assets of Class to get a list of say SaveGame_Character Class to get all character saves.

I will be dealing with A LOT of saves, such as worlds, locations, characters, objects etc. Think RPG, but the user is building eveything. And even a character would be seperated into separate parts of data such as Images, Character Profile, Timelines, Inventory etc., so that each piece of data can be loaded into memory when needed.

The only way I see to load specific save files (blueprint only) is to load them ALL, then loop through the results and attempt to cast to the needed save class. However, from my understanding, as soon as I cast, it loads i into memory.

I could do a separate master save to record metadata for saves, but I would really like to avoid this, and jut be able to directly get list of saves from specific folder or class.

I want the user to be able to copy/paste save files to/from each other and be able to load them by reading the files in directory.

If I have a base class Save_Base that all saves derive from, and have a single string variable for type, and cast only to the base class when iterating through entire save list, it wold only load the info for base class into memory yes/no?

I have also considered a naming convention for slot names, so I can parse the strings to get the “type” of save directly from file name and end up making very long file names

I could be talking about 100’s or even 1000’s of save files.

Structures are a great way of grouping, accessing and storing a lot of complex data. Structures are also great for the savegame class. You can create a structure holding all of you character data ie location, equipped items, rotation etc and others with location or world data. This way you can access the structures independently without retrieving data that you do not need.

Structures are terrible, and cause many issues down the road if they change at all, not to mention, all of the data in the structs will be loaded when save game is loaded. The data needs to be separated so it can be accessed and loaded into memory separately.

Multiple saves is the only way this can be done as far as i know, I need a way to filter saves though, as all a saves go to same folder. A terrible alternative may be to use manually move save files to a specific directory once they are saved, but it would be especially handy if there was a simpler way of doing this through SaveGame to Slot Node.

DataAssets are much better than structs, although I still end up using some very base level structs.

I then have a separate save class to save the Data Assets Data to Disk (User can create Assets from defaults, modify and then save the modified asset to the save game file).

I am guessing I will be forced into creating a SaveGame Subsystem to handle saving everything, and have it save metadata for saves with slot ID’s to quickly filter saves and load a list of particular save types etc.

You’re really hamstringing yourself here. Blueprint isn’t great at “complex”, but I’ll try to help as much as I can.

Correct, save games are not assets they are just files. Assets are specifically files that you create in the editor that you’d ship with your game. I don’t think the asset manager/registry even does anything for ini files, I think there’s a separate file thing for those.

Are you sure? I could have sworn you could give a slot name like “directory/savename” and it would produce a “savename.sav” file in the subdirectory. I’m not sure how you end up reading it back though. When calling GetSaveGameNames I’m not sure how it handle subdirectories.

Fair warning, this is not usually how saves are managed. Saves tend to have all the data for a single playthrough in one file, not broken up across different files that need to be managed and loaded separately.

Yup, if you want any information out of a save you’ll have to load it to inspect any data. This is just as true for a save system written in C++ as it is for one written in Blueprint. There are ways that you can sort of stream information out of the file so that you can read it without loading the whole thing, but a) you have to be in C++ and b) it doesn’t work on every platform.

You are confusing two different memory issues here. As soon as you load a save file you incur the memory cost to have the instance available to manipulate regardless of you doing any casting. Casting just introduces a hard reference to the casted-to type (B) in the blueprint with the cast (A) so that if you have (A) loaded, you have (B) loaded too. In some cases, like pawns with expensive meshes and the like this can be a real problem to have the blueprint loaded when you’re not using it. A save game class wouldn’t have those kinds of references so it would cause you problems like that.

No, the whole file would be in memory. You would just have made it so that when your one blueprint is loaded (A from the previous example) you wouldn’t have to load the (B) blueprint, you would only have to load (B)'s parent. But it’s not actually causing only the parent portion of the file to be in memory.

This can work. Back on the original Saints Row, it was very, very slow to read save files from the hard disk in order to populate the list of save files (Xbox360, PS3). But it was not that slow (still slow, but way faster then a file read) to get all the filenames. So we encoded all information needed for the UI into the filename, like indexs into image arrays, play time, dates and a bunch of other stuff. It was pretty slick. But you potentially have to worry about filename lengths and what characters are valid for filenames. It can also be brittle if you need to add new information after saves already exist that you don’t control.
For Marvel’s Midnight Suns, our custom save solution used the partial file reading trick so that our save files consisted of two objects, a header and the save. The header contained all the UI information and we could just load that part when enumerating all the files. But it’s not something you can do if you’re only using Blueprint.

Nice how you broke reply into pieces, how you do that?

Re:

Blueprint can be complex, I don’t understand why everyone gives blueprints so much hate and disrespect. I do not know C++, able but don’t have the time. I am a solo dev, I need a working prototype, and then when I get money I can hire a programmer to convert to C++ for a solid final program. If code is done properly, blueprint can be easy to read and understand, can be complex, and can be plenty fast for a large variety of games and applications.

Oh man, THIS would be a life saver! I have not tried this, and I can’t believe I have not come across info on it! I will try this!

Saves are meant for saving data. Period. And with blueprints, savegame is the only option. This is not your “typical” situation. I am not saving play through data for a game, I am saving individual assets a user creates in the program, and I need to be able to selectively load data, not a crap ton of data in a single file.

I watched an Unreal Live Stream where they specifically at one point discussed casting to a lower class (higher?) to prevent loading stuff into memory. So, thats where I was getting that from. For instance, if I have a base class “Character_Base” with a string ID as only variable, and a child class “Character” with references to textures etc., but I cast to “Character_Base” it would not load all the textures etc., just the string ID. I do not know how to actually test this as I have not done much with profiling etc. yet.

Yeah, i think at this point, I can save files to directories for quick categorization (if your suggestion works), you can use Blueprint File Utils Node “Find Files” to pick a specific directory and even filter file extensions (is it possible to change a save slot extension also when saving it to create custom extensions?). Then, I will use String Parsing on Slot Strings to add high priority filters. Then as a 3rd level for complex Save MetaData, in my base CaMTSave_Base class (for all saves) I have a String-String Map variable for MetaData. So, once I have gotten all files in a directory, parsed the slot naes to get a list of specific saves, I can then loop them and cast to base class to get meta-data, and, if it is how you say and everything will be loaded into memory, I will have a separate Save specifically for my Save Subsystem, where I can go through and collect MetaData for all save files, and only update it when needed since that would be time consuming.

Then, as last stp once I have a list, I could get the MetaData from the Subsystem Save Slot, and that MetaData could include info to display on a Open Asset UI Dialog Box for choosing which one to load/open.

Highlight text in the message, select “Quote” and it will insert the snippet part.

No hate here. I think blueprints are essential and absolutely have their place. I don’t think that everything needs to be done in C++. However it’s also true that there are tools, patterns, algorithms that just aren’t possible from Blueprint. But I don’t want to derail this into useless another Blueprint vs C++ debate.

Ah, okay that does change things. You’re not talking about save games, you’re talking about User Generated Content (UGC). I think there are plugins that let you use a more classic file system approach to files if you’d like to bypass Unreal’s save system for file management. But otherwise I supposed you’re stuck with the limitations of save games to do something it’s not really intended to do.

That’s great, but it doesn’t matter. You’re still confusing the issues. They are talking about the hard-reference issue that I referred to before. That type of loading is concerned with what gets loaded when there are no instances of the thing (Character or Character_Base). To load your blueprint with the Cast, it has to load the blueprint you’re casting to which will also load all of it’s hard references. This is why casting to Character_Base without those hard references won’t cause those to load when loading the blueprint with the cast.
But we’re not talking about that type of loading. Ignore the cast, if there is an instance of Character that has been created in your game all of it’s references will be loaded into memory. The Engine doesn’t load an unload parts of object instances based on the blueprint that is trying to use the object. This is the type of loading we’re talking about with a file. The whole thing is in memory regardless of the view (ie variable type) that you’re accessing it with.

Not if you’re using SaveToSlot. You’d have to find an API/plugin that gives you more control over files than the save game system.

As you pointed out yourself, this will make it difficult to copy files around and share them across users & developers. It will also be possible for it to get out of sync if the project crashes at the wrong time. You’ll want to update it a lot, like when the file on disk is newer than the time in the meta-data file, if there’s a file on disk not in the meta data, or whenever you actually load a file (since it’s loaded you might as well update the meta-data). Also, don’t overlook the usefulness of cheats that can forcibly rebuild the whole thing from scratch. It may be time consuming to do all at once, but if someone triggers it manually that should be okay. This will help with cases where you’re not updating the meta-data in all the right places yet and someone’s like “Why’s the metadata wrong?”.

Ok, I see what your saying. This helps a lot to clarify. This is why I wanted to separate saves into “parts”, for example character profile, which is data pertaining to every character, so instead of loading it for every instance, I want a reference (Save Slot String ID), so that I can have a separate Chaacter Save, to store only the strings so I an lightly load thousands of characters, however, the base class has a Metadata String->String Map, so I can iterate “quickly” through all the saves, get the metadata and store string references into variables sorting saves into separate variables, a map of string->Struct_String->StringMap (Save Slot String ID → MetaData) and this can be saved in the Subsysem SaveGame.

This way, I can detect if an update is needed and do this then to update the save, or it will just read the data from the save on the next load (example, new DLC has been installed, new user content has been installed (Images, FBX files etc.) Thats my thinking any how.

I have looked into these, but I avoid plugins AT ALL COSTS. I am solo developing a very large and complex program, and I have made the mistake of falling back on plugins to make life easier, to just end up having plugin developer not update plugin. I did have to resort to using Real Time Import/Export plugin to import FBX files at runtime (So user can import their own avatar 3d models for characters) which are widely available for free on the internet. Luckily, they have still been updating the plugin, and I need one for EOS as well :frowning: It will take me years to develop this to a point I can make it to crowd fund to get enough money to pay programmers to finalize it. But honestly, with the exception of the couple plugins, everything should run fast enough in blueprints for production as this is a multiplayer turn-based program, a table-top RPG campaign manager and suite of GM Tools

Yeah, I was thinking of having an option for user to specify (push button) to have program do the time consuming process of building the Metadata Save from scratch, and was hoping as well this would help alleviate any data reference mis-matchiing and give a solution to correct it when it happens. I tested, and indeed you can specify/create directories using Save Game to Slot which is infinitely helpful in my case.

Thanks for the great responses, this helped me a lot and taught me a lot

The big take away here was specifying a directory when using Save Game to Slot node in blueprint, and it will create directory if does not exist, and then by enabling the engine plugin “Blueprint File Utilities” you can use the Find Files function to retrieve an array of files in a specified directory.

I could have a directory for “Characters” and a character Save would retain only string references to the parts of Data a Character has, such as Character Profile, which could be a Data Asset on Disk, or a SaveGame file with the needed information to reconstruct the Data Asset (Data Assets can be used in memory to dynamically edit variables, then output to a SaveGame, and as well, 3rd Party Developers/Modders can easily create Data Assets for static data such as default characters etc. in DLC Packages).

If I read the Character files, and can load minimal data into memory and get a lot of information regarding the character as Key-Value pairs in a Metadata String-String Map variable, as well as string references to the data parts so that they can be loaded only when and if needed. (the program will often not need any visual data pertaining to a character, so can avoid loading)

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.