Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Unity3d PlayerPrefs Problem on Updating Build

A topic by SteBee created Dec 30, 2021 Views: 1,411 Replies: 10
Viewing posts 1 to 3
(1 edit) (+5)

Hi, 

after running in the problem of persistant saving of Unity3d PlayerPrefs when updating the game (manual or via Butler) I found a solution for me I want to share to others. You can find it here:

https://stebeegames.itch.io/feed-the-bunny/devlog/329860/unity3d-playerprefs-pro...

When I released my game on Itch.IO I was so happy that everything was so easy.

In my code I used the Unity3d playerprefs to save the game date between sessions. This worked in local tests and worked with my upload as well. I tested on Chrome, Edge and Firefox and everything seemed to be fine.

Then I uploaded a new build and my Odyssey began. 

I recognized that my save data was lost with the new build and started to investigate the problem.

Here what I found out:

Unity3d stores the playerprefs in web in the indexedDB of the browser. For the key it uses the build information of the project AND the domain from where the game is started.

On the first view this shouldn’t be a problem because the URL seems to be the same after a new upload.  (v6p9d9t4.ssl.hwcdn.net)

But the html sources are saved in a new subfolder with every new upload.

Old:

"/idbfs/cc364b4e4e0339afea5d9fd20484424d/PlayerPrefs"

New:

"/idbfs/5e35eebe0dd368392ea9e2e025d33b68/PlayerPrefs"

This is why the key in the indexedDB is new generated and your game can’t find the old playerprefs.

Way to my solution:

I started to search for “Unity3d playerprefs indexedDB Itch.IO” solutions in the web, on itch and in several forums. I found out that: “Yes, there is a problem.” is the most common answer.  So, I decided to store my game save data in the browsers Local Storage instead of using the build-in-solution. But how?

I started to search for “Unity3d C# Local Storage” solutions in the web, on itch and in several forums. I found out that: “This is possible and totally easy.” But how?

I didn’t find a solution to do this from my C# code directly and only found java or js solutions. So, I decided to use a js plugin to do so. But how?

I found a Unity3d sample project (CallJSFromCSharp) sending  browser messages in the web via html and js and started my experiments. After some hours of testing, crying and finally understanding what I’m doing I switched to my problem. Addressing the Local Storage. After some more hours of testing, crying and finally understanding what I’m doing and found a solution for me. And then I found out that it doesn’t work on Firefox. 

After even more hours of testing, crying and finally understanding what I’m doing I decided to use the build-in-solution for Firefox knowing and accepting that the save games on Firefox are lost between sessions. 

So now, I’m saving the game data in Chrome and Edge to the Local Storage and use the Standard Unity Playerprefs for Editor.

My solution:

I created a jsLibrary.jslib file with:

This file is stored in my Unity3d Assets\Plugins

With this settings in the inspector:

In the C# code the functions are addressed like this:

For save I use:

For load I use:

The Unity editor uses PlayerPrefs 

 

Chrome and Edge use the Local Storage:

I’m sure there must be easier solution for my problem but I didn’t find any. So I can live with my “workaround” and hope players can as well. 

I look forward to comments on how other developers have solved the problem and hope I can help one or the other beginner (like me) with my solution.

Thank you for reading so far and apologizes for my English. I hope you understand anyway.

(2 edits) (+2)

Hi SteBee, I ran into this problem as well, however I believe there is an easier solution:

https://itch.io/t/140214/persistent-data-in-updatable-webgl-games

I'm no web developer, but it seems like we can write to any folder inside the  browser's "idbfs" directory. I did something like this:

private string GetSavePath(string filename) {
         var path = Application.persistentDataPath;
  #if UNITY_WEBGL && !UNITY_EDITOR
         path = "/idbfs/YourGameNameOrAnythingHere";
          if (!Directory.Exists(path)) {
             Directory.CreateDirectory(path);
             Debug.Log("Creating save directory: " + path);
         }
 #endif
         var result = Path.Combine(path, filename);
         Debug.Log("Save path: " + result);
         return result;
}

Then you can use C#'s easy to use FileStream, BinaryFormatter, File.Open(...) etc APIs to serialize class data as usual.

I've confirmed this works with my little (soon to be released) Unity (2021.3) WebGL game that new builds correctly persist data as expected.

Verified in Chrome and Edge on Windows and Safari on macOS and iOS.

Hope this helps!

(+1)

Hi Paul, 

Thanks a lot for your solution.
I will try this with my next game.
Looks much easier than my workaround and should avoid a lot of problems on other webpages as well.

Thanks again and kind regards,

SteBee

(+1)

After a little more testing, it turns out IndexedDB might not actually be supported in Safari.

Safari does not support IndexedDB for content running in an iFrame.

https://docs.unity3d.com/2019.1/Documentation/Manual/webgl-browsercompatibility....

I was seeing it work at first, however when I close the Safari app the data is no longer persisted.

I'm not sure if it's possible to embed games on itch without using an iframe?

(2 edits) (+1)

As far as I can tell, indexedDB as well as localStorage are not supported on safari and by extension mobile. I believe Safari in general will only keep a file stored on the browser I think for up to a week. In regards to mobile, there are more problems. While you can make WebGL run smoothly on mobile, when it comes to saving at least on iOS, other browsers are actually built on top of Safari so they have the same saving issue. From what I can tell the browser will keep the file where you store it as long as you don't close out the entire app. You can close/switch tabs, but once you actually close the browser app you lose the data. 

This has been a big headache for my project, I'm using version 2022.1.23f1 if you care to compare. I actually don't own a mac and thus cannot inspect the page on iOS, but I have a crappy little mac mini showing up this Monday 10/9/23 so hopefully I can get to the bottom of this.

(3 edits)

Thanks for your details notes, what you described matches what I've been experiencing too, and it's reassuring to know it wasn't something specific to my code/project. I'm currently using Unity 2021.3.16f1 and just published my game here https://paulz.itch.io/roundy-round.

It's definitely confusing/misleading that all iOS browsers are essentially Safari under the hood because they are required to use Apple's WebKit framework. I even tried disabling "Prevent Cross-Site Tracking" and "Block All Cookies" in iOS Safari settings but it made no difference (Godot web publishing has some notes about IndexedDB). I don't have an Android mobile device to compare. Interestingly even when I updated Chrome (on Windows) recently I noticed my game's IndexedDB persisted data seemed to get deleted.

I even considered using PlayFab or some server-based storage solution, but if you want to use "anonymous login" for convenience so your players don't need to create or remember an account name/password, I don't see how that's possible because you'd still need to persist a GUID in the browser somehow, without using IndexedDB I guess.

(1 edit) (+1)

After a bit more research I have an answer. Basically, on itch and iOS, you cannot save to the browser and have it extend beyond the current session. The iframe and the page itself are two different domains, so its a CORS problem. The solution would be a server that users make an account/sign into, which is not ideal for quicker/more arcadey titles. For websites other than itch, there is hope for webgl games saving on iOS. If you own the page itself as well as the webgl, you can communicate from the iframe to the page to post data, this method uses window.postMessage which you can read up on here: https://www.teamsimmer.com/2023/05/02/how-do-i-use-the-postmessage-method-with-c...

So other than itch decided to give us access to our pages JS, we're out of luck. I also considered using cookies but we're limited to 4096ish bytes and I don't even know if we can do that from the iframe. Honestly I saw that the size was that small and figured it wasn't worth it.

I guess the best solution would be to throw your game up on a little site so you can handle saving to browser, then include a link to that page on the itch and hope mobile players find it? Or make a real iOS build and put it on the appstore. Neither solution works well for quicker/shorter titles.

Just before posting I found this post (https://itch.io/t/635029/enable-a-way-for-browser-games-to-access-the-username-o...) where someone asked Leafo the founder of itch if he would consider adding support for window.postMessage, it looks like a no but he was open to extending the current JS API. Maybe with our powers combined we can summon him here and get a mobile specific JS way to read/write to indexedDB/localStorage.

(+1)

In my workaround I split my savefile into several cookies. This allows you to bypass the memory limit.
But of course that's not a nice solution either.

Ah nice, any idea if that works for iOS/iFrames?

Never tried.
 If you get it to work with one cookie it's maybe worth a try.

---snip---

        ms = new MemoryStream();

        xmls.Serialize(ms, gd);

        var helpcvalue = Encrypt(Encoding.UTF8.GetString(ms.ToArray()));

        ms.Close();

        try

        {

            int helpcvallength = helpcvalue.Length;

            int helpcvalAnzahl = (helpcvallength / 4000);

            string[] helpcvalsplit = new string[helpcvallength];

            setCookie(gd.GameName + "A", (helpcvalAnzahl + 1).ToString(), 500);

            for (int i = 0; i < helpcvalAnzahl; i++)

            {

                helpcvalsplit[i] = helpcvalue.Substring(i * 4000, 4000);

                setCookie(gd.GameName + i.ToString(), helpcvalsplit[i], 500);

            }

            helpcvalsplit[helpcvalAnzahl] = helpcvalue[(helpcvalAnzahl * 4000)..];

            setCookie(gd.GameName + helpcvalAnzahl.ToString(), helpcvalsplit[helpcvalAnzahl], 500);

            RetVal = "Cookie OK: " + gd.LastSaveTime;

        }

        catch (Exception)

        {

---snap---

(2 edits)

Yep, I've come to the same conclusion. It doesn't seem possible to persist data on itch on iOS or Safari due to the iframe restriction. When I uploaded the same exact build of my game to my own site for testing, the data does persist (using IndexedDB storage).

I briefly tried experimenting with some Unity javascript plugin code to read & write cookies (and also tried window.localStorage instead) but neither one seemed to behave any different and also didn't persist data.

IF @Leafo can offer any help or insights here that would be amazing!