Save game system changed between 4.14 and a follow up?

Hello,

I currently have a live Android application in the play store working with 4.14. This application uses the UE save game system to keep game information.
As I was developing an update I noticed that my alpha testers no longer had access to their save games, I was now running 4.16
Made a lot of tests to understand if this was a save loading issue and after a lot of visual logging (Due to being unable to activate logs in shipping) I’ve come to the conclusion that the app simply cannot find where the old save games are.
I can also state that if I disable alpha testing and go back to live, I still have my old save games even though my app creates new files if it cannot find any.

Does anyone know what to revert so that I can reach out to the old save games? Is there another condition I am missing here ? (eg.: live app gets a special folder?, alpha and simply installing shipping directly use a different one?)

Thank you for any help you can provide,
Niktsuki

Google change permission policies for writing on android devices so now you have to require permissions to the user or the save ia no writen on disk after exit.
I tried adding the permission request on the manifest but is not working anyway so while there is no solution i had to implement an alternative solution.

Cheers good sir.

I thought about that too :smiley: so I tried removing the “use externalDir” option and requesting read/write permissions but after accepting them the problem remained the same (at least installing the APK directly)

Any other ideas that maybe I can try?

PS- There is a permission library that can be used in both cpp and blueprints, if you havent seen it I can point you to it.

Thank you!

If it’s a permission issue you shouldn’t be able to load/write anything from the SD, could you try to just write a file within your packages data folder? It’s someting like ‘<sdcard>/android/com.package.name/files/’.

UE should use that folder for the savegames too.

Hi Moss,

I added permissions but its not doing anything, I used the AndroidPermissions library to do so and as far as I can see the permission was granted, but still unable to read.

I’ve never seen it write to those folders, the only one I saw it write to is ‘<sdcard>/UE4Game/<projectName>/…’

The thing is that when I the save system and copy the SaveDir it is something in the order of ‘…/…/…/<projectName>’ but does not allow me to see in respect to what.

My worry is again that when I revert to the old version in live I still have access to the old save, which means that the new version is NOT overriding the old wherever it is…

If you have any idea what changed and how I can access the savegames there I will make a workaround to fix from old version to new… But I also heard that if I do so, the current UE configuration will delete savegames if the user uninstalls the app which is not great…

Thank you for any information you can provide!

Oh, forgot to mention that when using the new version (recent engine) everything still works and saves and loads normally, but as mentioned the problem seems to stem from the fact that the new version simply cannot access (or find) the old version save and therefore updated users lose all progress…

Its worth mentioning that the code never even attempts to load the archive because “DoesSaveGameExist” returns false.

The "“Use ExternalFilesDir for UE4Game files?” checkbox applies for distribution builds. Using this removes need to write to the sdcard and the files are cleaned up on uninstall instead of left around.

FPaths::GameSavedDir() + “SaveGames” is where it looks for saved game files. Look at SaveGameSystem.h.

Hi Chris,

This is helpful but doesnt tell me how I can recover the old files. I already disabled that button and allowed for read and write storage permission, and the query for the save game still returns false.

Are you able to tell me if there is any possibility for me to continue using the old path (the one back in 4.14). Clearly more changed than just this new toggle…

Thank you!

I’ve been trying to do my research again and noticed you did mention that this “would be added as a checkbox” on 4.15, I guess this is definitely where my problem started. Please allow me a couple of questions:

  • What should I do to be able to migrate the old save games to the new location?
  • You mentioned that “Use ExternalFilesDir for UE4Game files?” only applies to shipping builds, is there a way I can make it apply for all builds so I can test this consistently?
  • If the ExternalFilesDir is deleted, does that mean that everytime I install the apk through ADK will it erase my files? What about updates, are they considered uninstalls and installs?
  • Is there a way I can change the folder which it is saved to?
    This information will help in me addressing this issue… Thank you for your time.

Applying it to distribution builds was done so you didn’t lose saved games when iterating (APK can get uninstalled which would delete save games). Updating an APK from the store does not uninstall; it updates in place.

GFilePathBase (see AndroidFile.cpp) is the root for logs and saved games. This is set to getExternalFilesDir instead of the sdcard if the checkbox is enabled.

Leaving the checkbox unset shouldn’t have changed anything. I do see a change to SaveGameSystem.h which removed an extra / in the GetSaveGamePath() back on 3/23 of this year.

Thank you so very very much,

I think I know what to do from here to at the very least be able to recover the saves and probably migrate.

Thank you insanely, you’ve all been a brilliant help!

I am trying to do the exact thing Niktsuki is, migrate save files from the old folder to the new one used when bUseExternalFilesDir is checked.

What I would like to do is:

  1. Check new folder -> Does save exist?
  2. If not, check old folder -> Does save exist?
  3. If so, load into save object and save in new folder
  4. If not, create fresh save file in new folder.

This requires the GFilePathBase to change at runtime, is that possible? I don’t see any way to set it, and it seems to be set behind the scenes somehow. (gets a bit above my head at this depth)
Or is there another approach I am missing?

Been struggling and no one has answered, so here is an update.

What I am trying to do is add a function to SaveGameSystem.h called GetOldSaveGamePath that returns the sdcard path. (for a one time migration)

With the external dir option checked, in AndroidJNI.cpp, GFilePathBase is overwritten with GExternalFilePath. So as far as I can see that info is destroyed. Maybe something in FPath can still access the sdcard directory? I haven’t been able to figure it out yet, as FPath has alot of stuff in it and is complex to dig through. (I end up going in circles)

Looking at two possible approaches, neither of which are working for me:

  1. Create a new global variable and store the old path in it before it gets overwritten.

Problem: I don’t know how to access these global variables in SaveGameSystem.h, it isn’t really working like other global variables do. Also, even if I could, I don’t know for sure if it is a bad idea to hardcode like that without going through the paths system

  1. Figure out which function in FPath returns the old sdcard path and use that

Problem: I am not familiar enough with the naming convention of FPath to know if one of those functions will return what I need. Also I am not sure if the global path is the root of everything, and once set you cannot really go around it via FPath.

Please Chris if you are reading this, give me an answer. My game is stuck until I solve this!

Something like this should work (might have errors since I haven’t tried compiling it):


void CopyAllSavedGamesToExternal(bool DeleteOriginalFiles)
{
#if PLATFORM_ANDROID
    extern FString GOBBFilePathBase;
    FString OriginalSaveDir = GOBBFilePathBase + "/UE4Game/" + FApp:GetProjectName() + FString("/Saved/SaveGames/");
    FString NewSaveDir = FPaths::ProjectSavedDir() / "SaveGames/";

    // do nothing if trying to copy to same directory
    if (OriginalSaveDir.Equals(NewSaveDir))
    {
        return;
    }

    IFileManager* FileManager = &IFileManager::Get();

    // do nothing if original savedir doesn't exist (never had files)
    if (!FileManager->DirectoryExists(*OriginalSaveDir))
    {
        return;
    }

    // iterate over all the files in original save directory
    TArray<FString> directoriesToIgnoreAndNotRecurse;
    FLocalTimestampDirectoryVisitor Visitor(FPlatformFileManager::Get().GetPlatformFile(), directoriesToIgnoreAndNotRecurse, directoriesToIgnoreAndNotRecurse, false);
    FileManager->IterateDirectory(*OriginalSaveDir, Visitor);

    for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt)
    {
        // read the file contents and write it if successful to external path
        TArray<uint8> MemFile;
        const FString SourceFilename = TimestampIt.Key();
        if (FFileHelper::LoadFileToArray(MemFile, *SourceFilename, 0))
        {
            FString DestFilename = NewSaveDir / FPaths::GetCleanFilename(*SourceFilename);
            FFileHelper::SaveArrayToFile(MemFile, *DestFilename);

            if (DeleteOriginalFiles)
            {
                FileManager->DeleteFile(*SourceFileName);
            }
        }
    }
#endif
}


You will also need to make a change to AndroidFile.cpp to PathToAndroidPaths(). Add another case for GOBBFilePathBase like this:


            if ((AllowLocal && AndroidPath.StartsWith(TEXT("/"))) ||
                AndroidPath.StartsWith(GFontPathBase) ||
                AndroidPath.StartsWith(TEXT("/system/etc/")) ||
                AndroidPath.StartsWith(GExternalFilePath.Left(AndroidPath.Len()) ||
                AndroidPath.StartsWith(GOBBFilePathBase.Left(AndroidPath.Len())))
            {
                // Absolute paths are only local.
                LocalPath = AndroidPath;
                AssetPath = AndroidPath;
            }


Awesome, thank you. Will try this!