[PLUGIN] Savior

Okay I got to the point where I wanted to test my game on an Android device and it runs fine the first time I start it but after I close the game and try to reopen it will crash everytime the loadgameworld function gets called.
The problem is this only happens on Android and not on Windows.
I don’t know if your plugin supports Android but I hope you can point me in the right direction to fix this.

Android Crashlog
08-28 04:10:07.577  1786  1786 F DEBUG   : Build fingerprint: 'samsung/dream2ltexx/dream2lte:9/PPR1.180610.011/G955FXXSBDTJ1:user/release-keys'
08-28 04:10:07.577  1786  1786 F DEBUG   : Revision: '10'
08-28 04:10:07.577  1786  1786 F DEBUG   : ABI: 'arm'
08-28 04:10:07.577  1786  1786 F DEBUG   : pid: 1424, tid: 1586, name: PoolThread 3  >>> com.*******.******* <<<
08-28 04:10:07.577  1786  1786 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xb74eda84
08-28 04:10:07.577  1786  1786 F DEBUG   :     r0  b74eda80  r1  00000000  r2  00000001  r3  00006a80
08-28 04:10:07.577  1786  1786 F DEBUG   :     r4  b74e7000  r5  ceb83ec0  r6  00006a80  r7  00000060
08-28 04:10:07.577  1786  1786 F DEBUG   :     r8  b74eda80  r9  eb465ce0  r10 ceb84048  r11 caf0b068
08-28 04:10:07.577  1786  1786 F DEBUG   :     ip  c622f33c  sp  caf0b030  lr  c0340744  pc  c034074c
08-28 04:10:07.733  1786  1786 F DEBUG   :
08-28 04:10:07.733  1786  1786 F DEBUG   : backtrace:
08-28 04:10:07.733  1786  1786 F DEBUG   :     #00 pc 06e2774c  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FMallocBinned::Private::FreeInternal(FMallocBinned&, void*)+412)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #01 pc 06e749a0  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FMallocPoisonProxy::Free(void*)+108)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #02 pc 06e3381c  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FMemory::Free(void*)+180)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #03 pc 05b79830  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FSaviorRecord::~FSaviorRecord()+144)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #04 pc 05bb1694  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (_ZN12TSparseArrayI11TSetElementI6TTupleIJ5FName13FSaviorRecordEEE21TSparseArrayAllocatorI22TSizedDefaultAllocatorILi32EE25FDefaultBitArrayAllocatorEE5EmptyEi+356)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #05 pc 05bb33b0  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (_ZN12TSparseArrayI11TSetElementI6TTupleIJ5FName13FSaviorRecordEEE21TSparseArrayAllocatorI22TSizedDefaultAllocatorILi32EE25FDefaultBitArrayAllocatorEEaSERKSB_+44)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #06 pc 05b947c0  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (USavior3::SetSlotData(FSlotData const&)+176)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #07 pc 05b74b7c  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (USavior3::ShadowCopySlot(USavior3*, USavior3*, ESaviorResult&)+296)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #08 pc 05bcdb54  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #09 pc 05bcd6b0  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FAutoDeleteAsyncTask<TASK_DeserializeGameWorld>::DoWork()+780)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #10 pc 06e3ded8  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FQueuedThread::Run()+2200)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #11 pc 06e363dc  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FRunnableThreadPThread::Run()+152)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #12 pc 06d3be44  /data/app/com.*******.*******-Ng8-7o9V9t9mKcw1C7Usjw==/lib/arm/libUE4.so (offset 0x5a7f000) (FRunnableThreadPThread::_ThreadProc(void*)+80)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #13 pc 00064899  /system/lib/libc.so (__pthread_start(void*)+140)
08-28 04:10:07.733  1786  1786 F DEBUG   :     #14 pc 0001e329  /system/lib/libc.so (__start_thread+24)

How is your graph setup, do you call different load nodes from different locations or it’s always the same one call to the Load World node?

Do you have a crash if you change your Slot task mode to “synchronous task”?

LoadGameWorld node is only being called once from the gamemode. I switched it to the Sync instead of Async and that didn’t make a difference.
Below is the GameMode graph. The last image is the create slot instance function.


I also have a new crashlog
09-06 13:46:10.580 26474 26505 D UE4     : [2021.09.06-11.46.10:579][683]LogBlueprintUserMessages: [BP_SionGameInstance_C_2147482598] BP_SionGameInstance - Create Slot Instance - Success!
09-06 13:46:10.581 26474 26505 D UE4     : [2021.09.06-11.46.10:581][683]LogBlueprintUserMessages: [BP_SionGameModeBase_C_2147482506] BP_SionGameModeBase - LoadSlotMetaData - Success!
09-06 13:46:10.581 26474 26505 D UE4     : [2021.09.06-11.46.10:581][683]LogBlueprintUserMessages: [BP_SionGameModeBase_C_2147482506] BP_SionGameModeBase - LoadGameWorld - Begin!
09-06 13:46:10.630 26474 26505 D UE4     : [2021.09.06-11.46.10:630][683]LogOutputDevice: Warning:
09-06 13:46:10.630 26474 26505 D UE4     :
09-06 13:46:10.630 26474 26505 D UE4     : Script Stack (0 frames):
09-06 13:46:10.630 26474 26505 D UE4     :
09-06 13:46:10.785 26623 26623 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-06 13:46:10.785 26623 26623 F DEBUG   : Build fingerprint: 'samsung/dream2ltexx/dream2lte:9/PPR1.180610.011/G955FXXSBDTJ1:user/release-keys'
09-06 13:46:10.785 26623 26623 F DEBUG   : Revision: '10'
09-06 13:46:10.785 26623 26623 F DEBUG   : ABI: 'arm'
09-06 13:46:10.785 26623 26623 F DEBUG   : pid: 26474, tid: 26543, name: PoolThread 3  >>> com.**********.Sion <<<
09-06 13:46:10.785 26623 26623 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2
09-06 13:46:10.785 26623 26623 F DEBUG   : Cause: null pointer dereference
09-06 13:46:10.785 26623 26623 F DEBUG   :     r0  00000010  r1  00000011  r2  fffff000  r3  00000013
09-06 13:46:10.785 26623 26623 F DEBUG   :     r4  00000000  r5  00000073  r6  00001fff  r7  c88080e8
09-06 13:46:10.785 26623 26623 F DEBUG   :     r8  e59ae000  r9  00000000  r10 00001876  r11 c4135068
09-06 13:46:10.785 26623 26623 F DEBUG   :     ip  0000007f  sp  c4135030  lr  0000000c  pc  bc77d6e0
09-06 13:46:10.841 26474 26505 D UE4     : Assertion failed: OffsetFromBase >= 0 [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp] [Line: 1540]
09-06 13:46:10.841 26474 26505 D UE4     :
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]Assertion failed: OffsetFromBase >= 0 [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp] [Line: 1540]
09-06 13:46:10.842 26474 26505 D UE4     :
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: === Critical error: ===
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error:
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: Assertion failed: OffsetFromBase >= 0 [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp] [Line: 1540]
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error:
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: [Callstack] 0x00000000BC769744 libUE4.so(0x0000000006E13744)!FMallocBinned::GetAllocationSize(void*, unsigned int&)  []
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: [Callstack] 0x00000000BC7CA974 libUE4.so(0x0000000006E74974)!FMallocPoisonProxy::Free(void*)  []
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: [Callstack] 0x00000000BC789820 libUE4.so(0x0000000006E33820)!FMemory::Free(void*)  []
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: [Callstack] 0x00000000BB4CF834 libUE4.so(0x0000000005B79834)!FSaviorRecord::~FSaviorRecord()  []
09-06 13:46:10.842 26474 26505 D UE4     : [2021.09.06-11.46.10:842][683]LogAndroid: Error: [Callstack] 0x00000000BB507698 libUE4.so(0x0000000005BB1698)!TSparseArray<TSetElement<TTuple<FName, FSaviorRecord> >, TSparseArrayAllocator<TSizedDefaultAllocator<32>, FDefaultBitArrayAllocator> >::Empty(int)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000BB4CBE58 libUE4.so(0x0000000005B75E58)!USavior3::PrepareSlotToLoad(UObject const*)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000BB4E84E8 libUE4.so(0x0000000005B924E8)!USavior3::LoadGameWorld(UObject*, bool, ESaviorResult&)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000BB4E9C60 libUE4.so(0x0000000005B93C60)!USavior3::StaticLoadGameWorld(UWorld*)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000BB52FBA4 libUE4.so(0x0000000005BD9BA4)!TBaseUObjectMethodDelegateInstance<false, USavior3, void (UWorld*), FDefaultDelegateUserPolicy>::ExecuteIfSafe(UWorld*) const  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000C04B05DC libUE4.so(0x000000000AB5A5DC)!TMulticastDelegate<void (UWorld*), FDefaultDelegateUserPolicy>::Broadcast(UWorld*) const  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000C10DFAEC libUE4.so(0x000000000B789AEC)!UEngine::LoadMap(FWorldContext&, FURL, UPendingNetGame*, FString&)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000C10DA4C4 libUE4.so(0x000000000B7844C4)!UEngine::Browse(FWorldContext&, FURL, FString&)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000C10DC318 libUE4.so(0x000000000B786318)!UEngine::TickWorldTravel(FWorldContext&, float)  []
09-06 13:46:10.843 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000C057AE4C libUE4.so(0x000000000AC24E4C)!UGameEngine::Tick(float, bool)  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:843][683]LogAndroid: Error: [Callstack] 0x00000000BB3F0CA4 libUE4.so(0x0000000005A9ACA4)!FEngineLoop::Tick()  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error: [Callstack] 0x00000000BB3E49D0 libUE4.so(0x0000000005A8E9D0)!AndroidMain(android_app*)  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error: [Callstack] 0x00000000BB3F9C50 libUE4.so(0x0000000005AA3C50)!android_main()  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error: [Callstack] 0x00000000BB42D0E8 libUE4.so(0x0000000005AD70E8)![Unknown]()  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error: [Callstack] 0x00000000E54F289A libc.so(0x000000000006489A)![Unknown]()  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error: [Callstack] 0x00000000E54AC32A libc.so(0x000000000001E32A)![Unknown]()  []
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error:
09-06 13:46:10.844 26474 26505 D UE4     : [2021.09.06-11.46.10:844][683]LogAndroid: Error:
09-06 13:46:10.853 26474 26505 D UE4     : [2021.09.06-11.46.10:853][683]LogExit: Executing StaticShutdownAfterError

If you need anything else let me know.

@MrMightyErik apparently your device doesn’t like a call to System->Reset() on Savior.cpp line 325.

I don’t know yet what to do about this, need to learn more, but if you know how to edit source code and rebuild DLLs, you might want to try commenting that line out and see if that helps you.

After some tests, I am submitting an update with that line commented out along other minor changes.
Let me know if that is resolved for you once Epic process the new source files.

Okay I will wait for that because if I comment out lin 325 I get a different error while compiling.

PM me the logs so I can take a look later!

Updating to the latest version from the marketplace has fixed my game crashing on my android device.
So if anyone else has the same problem just update the plugin to the latest version.
Both 4.26 and 4.27 versions work.

1 Like

Hi. I have 2 questions:

  1. Im trying to save a soft reference to another dynamic actor in a dynamic actor. On the On Prepare To Save event, I convert the hard reference to a soft reference, after which there should be a save from the savior. After loading on the On Loaded event, I try to get back a hard referenjce with a soft one but I get None. Both dynamic actors have SGUIDs and a Procedural interfaces. In this case, the savior in the logs does not give errors or warnings. I save and load the save with the Save / Load Game World (Async) command. SaveGame flag on variable is seted. What could be the problem?

2)Is it possible to somehow determine the sequence of loading / respawning of the actors? For example, I have actors who have a Child Actor, and a Child Actor on BeginPlay receive a reference to their Parent Actor and then interact with him. And apparently during loading, they are loaded chaotically, since I periodically observe that the Child Actor returns None when receiving the Parent Actor reference, so it turns out that the child was created earlier than the parent. Of course, i can send a command to the child actor after end of the spawn of the parent actor using an interface or an event, but it’s rather dreary to think over your own BeginPlay and Load events for each such case. Is it possible to define the sequence of loading the actors by classes or otherwise, except to manually load them?

@CHERI1230 the problem there is that your property is restored from save before the actor finished respawning.

Please read this related topic for more info:

Ok. As for streams and their effect on loading, I understood. What about the soft reference load? I’m trying to get a soft reference in the same actor. And threads, as I understand it, do not affect it. All other data such as int, bool, Enum are saved and loaded successfully only soft reference after loading is None.

That’s what I mean, the Property is loaded before the actor is finished spawning.

Use the actor to set itself in the soft reference after its finished respawning.

See there “On Finished Respawn” event.

Apparently I have not correctly explained what I am trying to do or I am trying to use soft reference for other purposes.
I have a pump in the game that pumps fluids. The pump is connected to tanks and has In and Out directions. When the pump turns on, it is pumping liquid (resource) from Out to In. The pump is dynamic. The player can buy or sell it, move it, etc. And so, when the pump is running, something like this happens:
(ref) TankOut.Liquid = (ref) TankOut.Liquid - 1;
(ref) TankIn.Liquid = (ref) TankIn.Liquid + 1;
And after loading, I need to restore links to TankOut and TankIn in order for the pump to work correctly. Initially, the TankOut and TankIn refernces are assigned when the player connects the hose.
And I cannot assign In and Out tanks during the respawn event, because tanks may be 0, or maybe 1000, and which one will be In or Out i don’t know, it all depends on the player. I need to restore the link to those tanks that the player previously assigned.

The only way that I see is to assign an ID to each actor for which you need to restore the reference, then after the respawn do “get all actor of class” and check each one for the desired ID. Save the ID with the help of the savior.

Get Actors of Class is horrible for performance.

In a case like that, I would store in Player two properties of type ‘Guid’ marked SaveGame and, after loading world from save, I would pick the CurrentIn and CurrentOut Guid values and reassign the references searching actors by SGUID values:

https://brunoxavierleite.github.io/Savior2API.github.io/Savior3/nodes/FindActorWithGUID.html

Get Actors of Class is horrible for performance.

In a case like that, I would store in Player two properties of type ‘Guid’ marked SaveGame and, after loading world from save, I would pick the CurrentIn and CurrentOut Guid values and reassign the references searching actors by SGUID values:

[[SAVIOR] Find Actor With GUID ]

WOW, you and your plugin are incredible.

Thanks for the help. And one last little question. Based on all this, I understand that it is impossible to directly save the actor reference and the actor soft reference and they must be restored in some other way. This is true? I want to understand UE4 a little more from the side of game saves.

The issue there is caused by Async loading.
You could change the slot to synchronous task, but doing that would freeze the game (waiting for all actors to respawn) to mitigate the result you see, but doing that would still NOT avoid the property (ref) being read from slot before target actor exists… because your actors are dynamically spawned, so the plug-in cannot predict which property is going to be restored before the referenced actor was respawned.

Hi. The variable is not saved, I created a variable of type bool and on the On Prepare To Save event I write data to it (true) after which I expect it to be saved by the plugin. The event fires. But when loaded, the variable is set to default (false). In the save file, the variable has the desired value (true), what could be the problem?

You can enable logs / deep logs on your slot class and check the values being loaded from output log panel on editor.

I found a problem, but I don’t know how to solve it, maybe you can tell me…
As I understand it, the actor is loading too fast, unlike the loaded plugin data. I added a 1 second delay and the data was loaded completely. I tried different events,On Begin Play, On Begin Respawn, On Finish Respawn and everywhere the data was lost but with a delay everything loaded. I tried On Loaded, but as I understand it, it only works when the actor is already in the world and does not spawn while loading game. How do I get the event when the actor has received 100% of the data from the plugin?

It would be nice if you updated the event documentation to accurately describe how they work.

Also, a small question, if I save the SGUID for later restoring the dynamic Actor Reference, do I need to use Generete Once: SGUID or still use Make SGUID?

Respawned actors will receive On Finish Respawn event first.
Then load properties and component properties.

Then it execute the On Loaded event, however, there are components that might happen to be added later.

[SAVIOR] Procedural (brunoxavierleite.github.io)
[SAVIOR] Serializable (brunoxavierleite.github.io)


You will only be certain that everything was respawned, and all properties were read from file, from the Finished Load Callback of an Async Load Game World node:

Then you should start reading values, the callback is the last thing to be executed once the async task is complete.