[PLUGIN] Savior

By the way, I want to write a "How-To Build Serializable Inventory Systems with Savior "
since last year.

I only need to find the time to do it :s

Hey Bruno, thanks for the quick answer.

To be totally honest with you, Savior is being a headache to me.

At PlayerController I have this:

And the Testing Actor variable looks like this:

(As you can see, SaveGame is checked.)

That said,

  1. I start the game in PIE
  2. I hit Space Bar so my TestingActor is spawned and its instance is properly set against the TestingActor variable.
  3. I hit Q, so the game gets saved (the ‘Saved :)’ Print String is being displayed properly).
  4. I close the game.
  5. I open it again, still in PIE.
  6. I hit E to load it and it displays “Loaded :)” message successfully.
  7. I hit O in order to see if my TestingActor is valid and it says false, however, the Actor was properly spawned.

A few notes:

  1. My TestingActor is implementing the [SAVIOR] Procedural Interface.
  2. My TestingActor has an SGUID properly set at ConstructionScript:
    image
  3. I have an SGUID variable properly set as of type GUID in my TestingActor:
    image
  4. If instead of saving an Actor vs. a Float at my PlayerController, the value is successfully saved using the exact same process as described above. So this of course is something related to saving/serializing Actors.

That to say, I’ve spent all night yesterday and this whole noon today and can’t still save/load a pretty vanilla Actor. And I swear: I read all the docs, I dove into this forum thread, I tried my best to understand all the concepts… I mean, I don’t know what else I have to do to save this Actor.

And I’m sorry, I don’t know C++. Reading the Source Code is not an option because it makes no difference.

I wonder what others have done that I haven’t in order to be successful with Savior.

Also, another question that I have.

OnPrepareToSave in an Actor of mine is not being called when I successfully run SaveActor and WriteSlotToFile, but it is called when I call SaveWorld (Async). Clues?

“On Prepare XX” events are controlled by the Save/Load World thread.
When you execute “Save Actor” nodes, they do not initiate a new engine thread, so those events should not be executed since it’s code running in Main Thread only.


I will answer your post above, there’s several aspects of the Game Framework you are not considering (not really related with the plugin, but the way Unreal Engine runs);
I will try to explain what is missing with screenshots…

You have figured everything correctly, but there’s some aspects of Unreal Engine that you are missing;

Classes, member of GameFramework’s Game Mode group, are always dynamically spawned at runtime by Unreal Engine when you hit Play button:

Notice the "_C_0 " suffix on your Player Controller class… once you quit and play again, that will change to "_C_1 ", "_C_2 ", etc…

Your Controller class, if you want it to be a data container in save slots, and restore properties in it from a load process (such as that test actor reference), it is lacking a SGUID for itself, so you should setup one for the Controller:


With a SGUID on PlayerController class, that is resolved.


Now, there’s the complicated part. Multithreading.
When you execute a Save/Load World (Async) operation, you are running multithreaded code. This is complicated.

What can happen (and it is the case here), is that one thread can finish work before something else has finished. I mean, because you are referencing an Actor that must be respawned before inserted into the array, Savior will not sit and wait for Unreal to finish spawning this Actor. Something like this is what is going on in plugin’s “head”:

  • Begin Loading process (Async)
  • Player Controller instance found: load all its data.
  • Decision: Looking for Actor XX, need to respawn.
  • Decision: Set value of ‘TestActor’ reference, instance not found.
  • Skip, continue loading data…

Meanwhile, the Game World is there doing this:

  • Oops somebody requested to spawn this actor, spawning…

Savior’s own thread by this time is:

  • Welp, all things loaded, I’m done, shutting down this thread.

The recently spawned actor:

  • Okay guys, I’m ready! … guys?! Hello?

That’s kindda the reason why you can’t figure out why the TestActor reference can’t find the spawned actor. See logs:

Save

Load

The reference in your PController is loaded way waayy before the actor is done respawing.


How to solve this kind of race condition ?
It’s somewhat simple, you can hijack the “On Finish Respawn” event from the actor itself make it do a polite request to inject itself back into the TestActor reference within the Player Controller instance, for example:

The thing is: threading is a complex thing to deal with some times.
If there’s any doubt, let me know.


Looks like the update mentioned above never got to the Marketplace repository…
So I am submitting this again to Marketplace staff for review.

I am waiting for Epic to release latest version (3.5.8) for Unreal 4.26 on Marketplace.
With this update, I will also add to demo project a “Simple Inventory” component that can illustrate how to easily create an array of serializable Objects (items) that the plugin will save and load automatically for you:



The core of the entire inventory system lies on the fact that the “Simple Item” class is derived from the “Auto Instanced” class that is provided by the Savior plugin:

UCLASS(ClassGroup=Inventory, Category="Inventory", Abstract, HideDropdown, BlueprintType)
class SIMPLEINVENTORY_API USimpleInventoryItem : public UAutoInstanced
{
    //...
}


The Inventory Component class just holds an array of those objects, together with some helper nodes to add/remove instances of Items. Because Items are derived from UAutoInstanced class of UObjects, once you load a save data the Savior plugin understands that you give it free pass to respawn any instances of those items into the Array that is marked with the ‘Save Game’ tag within the Inventory Component class:

UPROPERTY(Category="Inventory", VisibleAnywhere, SaveGame)
 TArray<USimpleInventoryItem*> Inventory;


Also another key factor is the fact that both, the Inventory Component and instances of Simple Item classes, contain a ‘SGUID’ property that is properly initialized from each class ‘PostInitProperties()’ method:

if ( !HasAnyFlags(RF_ClassDefaultObject|RF_ArchetypeObject) )
{
	SGUID = USavior3::MakeObjectGUID(this,EGuidGeneratorMode::ResolvedNameToGUID);
}


You can check the code and blueprints when update is released by Epic, and I have also uploaded the Simple Inventory source code to GitHub that you can download from here:

(UE4) Simple Inventory (github.com)



In demo project there’s both interactable item actors to pick from world that add themselves to the inventory and input commands for testing:


Ctrl+Shift+L in Character Blueprint loads inventory data from any targeted Slot.
It’s using the handy ‘Load Actor Hierarchy’ function for that:




If you also want to examine the way that inventory widgets were put together, it’s good idea to take a look into these C++ classes:

SimpleInventoryWidget.h
SimpleInventoryItemWidget.h

And the Blueprint classes that compose an usable Item class.
I have create two blueprints as example:

BP_SmallPotion
BP_LargePotion

They can be found in Content/Savior2/Demo/Inventory directory.
Widget Blueprints can be found inside SimpleInventory’s Plugin Content directory:




Epic updated binary files and demo project was updated on Unreal 4.26 to include the inventory mentioned above.

Is there a way to clear and erase all saves easily. I am currently working on a game that uses a save per level (for instance if you go into a store that level is its own save and when I go to another space it is its own save). While the system works, I am unable to clear each level unless I delete the save and re create a new one which slows down testing.

Thanks,

While working on Unreal Editor, saves are stored into your project’s Saved\SaveGames folder. You can simply wipe them out using windows explorer.

Hi… can you please explain why did you create SGUID in constructor,Post load and Post Init Properties, and also why did you use MakeObjectGUID in SimpleInventoryComponent.cpp → PostInitProperties, should we not use MakeComponentGUID ?

I have only 1 inventory component, and a component is also an UObject so that’s good enough. The only thing that really matters is having a valid and unique FGuid value.

Also pay attention to the ‘HasFlag()’ checks, an archetype is a thing, a blueprint class is another, and a spawned component is another thing… all generated from the same class.

Thanks you for the explanations , I am facing another problem. I am unable to save / load Simple TSubclassOf Property inside a UStruct. Is it not possible to save , load TsubclassOf in a struct?

I think that area falls into engine source code. There’s a point in code where I use Epic’s Json serializer to convert a struct to text, if that isn’t working then it’s because the engine does not support it by default, and engine source code changes would be required.

When that is the case I often replace the type with a soft reference.

Can you please provide a simple code snippet or any kind of example , that would be really helpful.

You can find in engine several examples for soft class path; it’s a struct:

Submitted update for Unreal 4.27 plus a fix for a small regression in UE4.26:

  • Actors auto-respawned from save files are missing class full path info (a 1 line of code I didn’t merge from sources of Unreal 5 plugin, causing a silent bug)

Thanks for the help earlier with my question.

I have 2 other questions currently. I was wondering if there is some way I can save variables that are located in a “task” for a behavior tree. Basically in my game after a player says something to a NPC I have a variable that limits them from going down that dialogue branch again (I am using behavior trees for my dialog system).

My second question regards saving variables on actors in my level. I am currently using the save game mode and level functions when I am going from level to level (my game is an rpg game so the player is constantly going in and out of different levels") so the stuff on my player is working and saving fine but I cant seem to get actors in the level to properly save and load their information. For instance, when a player finds a new location I have a screen text pop up that is triggered by a overlap. I do not want that trigger to activate ever again however upon the player re entering the level it triggers it. I have tried implementing the save actors function but I am unsure if I am using it properly. Could you provide some guidance regarding this? thanks.

I have been thinking about Blackboards serializer, but I never got a response from the creator of Unreal’s Behavior Tree system on GitHub, ever; there’s some API access required that is simply private code, I haven’t tried it again since a long time tho.

You would have to manually feed your BT with values saved on character BP.


The plugin does not record the state of trigger volumes.

You have to create a boolean property, mark it “Save Game” and implement the Serializable interface.
From On Loaded interface event, check again the value of the boolean and disable trigger collision detection based on value of that boolean.

(see saved ragdoll effect in demo project, same process)

Great, I was able to fix the collision item doing what you said and I also set the default of the overlap event to be off unless it has never been loaded before which has fixed that issue up. In regards to the dialog I ended up just making a savable integer in the actor BP of the NPC I was talking to and made another dialog tree without the dialog I did not want said again. Then when that dialog is said in the current level it increases a integer which is then saved when you exit the level and upon reload a new dialog BT is loaded that is one +1 integer (I put the dialog trees into an array). Thanks for the help I will be back soon probably with some other questions lol.

1 Like