[PLUGIN] Savior

Hmm… but then how would parent/child relationship be resolved without guid on components?

I have component class with “_C” and some other component without “_C”. I think it should at least work for some components. We can figure out the other problems first and think about what we can do with “_C”. ^^

Right now, the biggest problem seems to be why LoadActor is getting called multiple times.

I will try another way, a function you can input your SGuid directly, so there’s no for each actor of class code…

That sounds like a good idea!

And please don’t forget about supporting StaticMesh type in the component. Without it, I may not even know if worked or not. ^^

And if your recommendation is to use Minimal Compression, I would appreciate it if you can add Scale. I prefer using Minimal if I have a choice. Complex looks pretty intimidating.

Cheers!

Can you send me screenshots of your StaticMesh setup?

If no properties are saved from it, it’s because its property are hardcoded in C++ and not marked with SaveGame tag.

Yup, just send you an email.

Ver 3.7.2.
In Funciton USAVIOR_LoadLevel::LoadLevel, there is a redundant line of code at the beginning.

OBJ->Target = USavior3::NewSlotInstance(Context,Savior,Result);

1 Like

That’s true.
That really shouldn’t be there. Fixed, thank you very much!

I changed PlayTime to store total seconds instead of passed Hours.
But now you will have to build a Timespan and format it to text to display it correctly:

@BrUnO_XaVIeR

Sorry if asked before, but we’re having trouble while loading save from our Persistent level.

Our persistent has multiple sublevels, and I did a quick test with “Save/Load World” function. When I save game in one of the sublevels, and load the world back, nothing from this sublevel gets loaded. Only changes from persistent are loaded.

Is there any proper guide about how to save/load game with level streaming?

Those functions does not automatically stream sub-levels.
If you want to work based on Levels, you are supposed to stream your Level then call on it:

“New Slot Instance” → “Read Slot from File” → “Load Level”.

With a reference, into the Load Level node, to the Map that you want to load.
(or name of the Level, depending on the node you use)

Is there a trick to loading procedurally generated actors quickly from save? Each one is currently taking about 0.5sec to load according to the rate at which they’re getting logged out. The actor is a mesh, and a couple components that seem pretty small to me, but I guess I could be wrong. I still see the delay even if I don’t execute any of the callbacks. The logs show one of these for every actor:

SaviorLog: {S}:: UNPACKED DATA for BP_GridMiniature_000002A10000026500000235000001CD:: {"MiniatureAssetPath":"/Game/MeshFolder/Mesh.Mesh","bCanBeDamaged":false}
SaviorLog: {S}:: Deserialized :: BP_GridMiniature_000002A10000026500000235000001CD

Also, the entire editor freezes up while these logs are being output, which makes me think there is some synchronous action blocking the thread, but I’m definitely using the async version of Load Game World. These are procedurally spawned actors so they have the SGUID property on them, but i don’t think that should be making a difference. Any suggestions would be greatly appreciated. Thanks!

Typo found.

In USavior3::SaveObjectHierarchy and USavior3::LoadObjectHierarchy

if (Object->IsA(UActorComponent::StaticClass())||Object->IsA(UActorComponent::StaticClass()))

if (Object->IsA(UActorComponent::StaticClass())||Object->IsA(AActor::StaticClass()))

In USavior3::SaveObjectsOfClass and USavior3::LoadObjectsOfClass

if (Class.Get()->IsChildOf(UActorComponent::StaticClass())||Class.Get()->IsChildOf(UActorComponent::StaticClass()))

if (Class.Get()->IsChildOf(UActorComponent::StaticClass())||Class.Get()->IsChildOf(AActor::StaticClass()))

In USavior3::LoadObjectHierarchy, the last operation is SaveObject, it should be LoadObject.

Thanks! I will test it today when I have a break.

The plugin must load the Mesh object before applying to the root component.
If there’s no reference anywhere else to the Mesh and it must be loaded from disk… then that’s going to take a while to load.

You can either keep the mesh pre-loaded or edit c++ plugin source to remove the calls to ResolveObject function.

I thought that might be the case so I set all the meshes to be the same one and the load time still scaled linearly with the number of actors I added to the level, which makes me think it’s not the mesh component. I’m only adding about 30 meshes and load times are 15+ seconds. Do you have any recommendations about where a good place to start doing time measurements would be? I’m a little newer to UE than I’d like to be. I think this is where some quick tutorial videos or better documentation would be helpful.

Also, does the plug-in save the meshes automatically? I was loading them up manually after the actors has been respawnes by the save system because I didn’t think that was the case. I’m just venturing in to Save functionality and your plug-in was so highly rated and seemed so much more practical that I just went for it instead of starting with the UE save system.

The plugin only records the Root mesh from the Save/Load Game World function.
It doesn’t make records of meshes for adjacent components, that you are supposed to manage yourself (Soft References to save/load/apply the mesh).

Without a sample project and detailed Log files with the loading process, it’s hard to say what is going on with your project.


Btw, you might be triggering multiple loading calls on the same actors over and over, depending on what you’re doing since you say you’re loading the mashes manually.

Understood about the sample project. I’m going to keep trying to figure out what is happening so I can learn a little more. If I find a bug I’ll set up some reproduction steps.

Interesting idea about the multiple loading calls. I’ll look in to that as well.

Overall though, spawning saved actors should be a fairly quick process right? I’m making a world builder so I’m going to have lots of actors in the level. I may try breaking the operations up such that I’m only saving a struct of the mesh path and transform and then spawn everything at runtime myself and use Savior for the other more complex save logic to come.

I appreciate all the help and your fast responses!

Yes, I have tested levels with over 2 million objects.
That delay should not happen. Data loading from saves should take a few milliseconds in general, unless your game is forcing load of meshes that are not referenced by anything, but the plugin thinks it must be loaded because it’s a Root component.

I tested on another map that wasn’t my main game map and everything loaded quickly as advertised, so I’m guessing there are some side effect of spawning a mesh that I didn’t realize. Going to go investigate that. Thank you!

So I think found the smoking gun. For some reason in Reflector::RespawnActorFromData the TActorIterator<UObject> used to find the actor’s owner is iterating 160k times for each actor in the level (about 100 total), so we’ve got an O(N^2) runtime. That results in about 297ms of run time. Given your statement about the 2 million+ object levels, I’m assuming there is a design decision I must have made that is producing way more UObjects than expected, so I’m going to investigate that some more tomorrow against the sample project.

Since all the UObjects are going to have to be checked for every procedurally spawned actor, it might make sense to try a caching strategy to speed things up. Maybe having a list of known owner objects that gets checked first, and if none of those objects match, iterate through all UObjects until you find the owner and then add it to that list? A hash is probably too memory intensive, but could also work.

1 Like