Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Persistent Data in (updatable) WebGL games

A topic by DynamiteWhale created Sep 05, 2017 Views: 12,925 Replies: 13
Viewing posts 1 to 11
(1 edit) (+4)

Hello Everyone,


I have spent some time trying to find a good solution for saving game-data (e.g. save games) in a way that persists also after an update on itch.io.

So far my experiments on itch have revealed the following - and please correct me if I am wrong on this:

Any update of a game (even when uploading the same zip/build as an update), will put the game into a different domain (not sure if this is the right word). What I mean is, that any data stored in an indexedDB (afaik Unity also uses indexedDB to store playerprefs), will not be accessible after the update anymore because the browser treats this as a "new" instance the game.

This only leaves some unconvinient options to store playerdata:

  1. Host own user accounts. Players can create an account with a username, and the game will save game data on a 3rd party server. Players can retrieve their savegame by entering their username/password.
  2. Save-game-to-file. Players can export the savegame to XML/Json or an encrypted binary to their drive. 

I have the feeling I am not seeing the obvious solution, or am understanding something fundamentally wrong with the way itch handles updates.

Thank you in advance,

The Dynamite Whale

Admin(+3)

Games are always served from the same domain, but their path in the domain changes every time you upload a new build. This is due to how our caching system works. Do you know if it's possible to change how Unity picks the path to save the game settings to?

I think in the future it makes sense for us to have an API to store game related things on an account basis.

(1 edit)

Hey,

I guess the new path is what creates this problem.

I do not know where exactly unity stores their "PlayerPrefs" but according to some posts on the unity forum #1 #2 PlayerPrefs used the IndexedDB.

I tried an alternative way by storing a JSON in unity's builtin persistentDataPath.

When I log the path to the browsers console in my test-project, the path I get is: "/idbfs/90b3e05603d8efe64ce5c035f88e6b1f"

Saving and retrieving the json fron there is no problem, but has the aforementioned issue when updating the project.


EDIT:

. In my test project I am also saving to playerprefs - and the path in the indexed DB for that, seems to be:

/idbfs/90b3e05603d8efe64ce5c035f88e6b1f/PlayerPrefs

- the JSON I save to the persistentpath ends up here: /idbfs/90b3e05603d8efe64ce5c035f88e6b1f/test.json

Other paths that unity seems to create or use:

"/idbfs/90b3e05603d8efe64ce5c035f88e6b1f/Unity""/idbfs/90b3e05603d8efe64ce5c035f88e6b1f/Unity/local.7f8d86c75cc3fab4aab80b5a6b4ef072""/idbfs/90b3e05603d8efe64ce5c035f88e6b1f/Unity/local.7f8d86c75cc3fab4aab80b5a6b4ef072/Analytics""

The persistentpath-saving I do like this (if it matters...)

C# using System.IO

        FileStream stream = File.Open(Application.persistentDataPath + "/test.json", FileMode.OpenOrCreate);          

        StreamWriter write = new StreamWriter(stream);
        write.Write(JsonUtility.ToJson(state));    
        write.Close();

I'm eager to hear if somebody finds a solution to this. 

I have a similar problem. One version of my game shows that persistentDataPath is "/idbfs" and another version shows "/idbfs/b647bc5784577....". This prevents me from loading the save file. I don't know what to do? Is this specific to itch.io ? I am trying to create an updated version of my game that will be published on a web games site.

Still no solution for this? I have the same problem...

My entire store is basically built around the ability to store your game's settings but my solution is to store it online under a user account. 

The big PRO here is that your data is synced across all devices you play on (i.e. you can play in your browser then pick up your phone and have the same progress/settings on there or vice versa). The big CON here is that you must store all their data in your own web space. Nevertheless, that is my approach.

I was actually surprised to find this topic because when you read the Unity docs (or at least back when I read them in this regard) they stated that the DataPath is relative to your game and can thus be overwritten but the persistentDataPath was defined as a path based on the platform you were playing on (i.e. the path is the same on all phones, on all Windows system, on all Mac desktops etc) specifically for the purpose of having your data persist between updates. If you are saying it isn't doing that then that truly is weird...

Thing is, though, the actual name of the project and developer determines where the data is stored so just make sure you don't change the name or the com.myname.gamename settings in the PlayerSettings and as far as I am aware, that SHOULD be the end of all your woes! :O 

Note that when you build the app, a GUID will be generated based on the Bundle Identifier, and this GUID will be part of persistentDataPath. If you keep the same Bundle Identifier in future versions then the app will continue accessing the same location on every update.

and of course on Macs

user data is written into~/Library/Application Support/company name/product name

Normal DataPath though...

WebGL: The absolute url to the player data file folder (without the actual data file name)
(3 edits) (+1)

Okay, so that was the responsible thing to say...now, let me teach you about a hack you can employ to solve your data saving problems.

So, Unity allows you to create a public field in your script, then lets you drag a prefab onto that field in the inspector and during runtime you instantiate that prefab and use it however you see fit... This is Unity 101 and everybody knows this.

What you might NOT know, though, is that you don't HAVE to instantiate that object before you use it. As long as you have a reference to it then you can use it directly.... this includes changing values in the prefab. the thing is, though, that if you didn't instantiate the prefab first then it means you are actually modifying the prefab that WILL be instantiated later but hose changes are forever. You cannot undo whatever changes you made...

...and here comes the interesting part...

...even between play sessions. Yup. Hit play, change any values you want, remove your hard drive and plug it into a separate machine, boot that up and start the game and still the data will be the values you changed them to. Copy the game to a flash drive and install it on your mate's PC and the first time he plays the game he will start with your new values. It would basically be as if you originally set those values when you were busy making the game in the first place.

Unfortunately, this is not the most helpful bit of advice because if you save player's settings this way then every single player will start the game with the settings of the last person who played the game. See? Useless... But since it sounds like this issue is related ONLY to people playing the itch.io hosted WebGL version and you are planning on moving the game to other platforms, well, maybe you might be prepared to take this drastic measure...

What about you create a class (THAT CAN NEVER CHANGE AFTER THE FIRST TIME YOU DO THIS!!!) that contains all the data you want to save for a player. I.e. score, the upgrade level of each weapon, the current ammo, world position, rotation, kill count, etc. All of it. Let's say you call that class PlayerData. Now simply create this script, create a prefab from it and make sure something points to it so you can access the prefab:

public class AllSavedData : MonoBehaviour {
    public Dictionary<string, PlayerData> data;
    public void SavePlayerData(string me, PlayerData mydata) { data[me] = mydata; }
    public PlayerData GetPlayerData(string me) { get { return data.ContainsKey(me) ? data[me] : null; } }
} 

Please note that I have not tested this but in theory this should work. This is incredibly bad coding and should only be used out of sheer desperation but, if I am correct with this then all you would need to do is find SOME way of uniquely identifying a user (PC GUID maybe?) and then you can store all their data as part of the game POST release. The biggest problem with this is that all player's data will load for every player and then you have to extract just the entry you want. This is why I say this is very bad code.

Edit: Come to think of it, I think this means that each time you update the game you will overwrite this prefab anyway so unless using Butler to only update changes somehow allows this to still work then good, I guess... but yeah, just know about this little bug / feature / hack and see if this can be of use to you. I highly advise against it but if you can't come up with a solution that works then what have you got to loose by trying, right?

The other, most safest way, is to save the data to your own server. A very basic and simple solution would be to write a super simple php script that does nothing but create a new file and save anything you sent it to that file. This way you can fetch back that file using the super simple:

string my_settings;
IEnumerator GetMySettings(string me){
WWW w = new WWW("http://mysite.com" + me + ".txt");
yield return w;
my_settings = string.IsNullOrEmpty(w.error) ? w.text : string.Empty;
}

So now you just have to worry about how to serialise PlayerData into a string and back again and hope and pray that someone doesn't use your script to upload code to your site (there are workarounds to prevent that so not that big of a deal) and there you have it. Save the data on your website as a small text file per user and you never have to worry about loosing any settings ever again.

Again, also a drastic move but just throwing some ideas out there.

(+6)

I've managed to work around this by saving to/loading from "idbfs/GameName" rather than Application.persistentDataPath in WebGL builds. I saw metinevren suggested that here too: https://forum.unity.com/threads/persistentdatapath-returns-different-paths-for-d...

HOWEVER, PlayerPrefs and a bunch of other unity stuff still use the persistentDataPath (eg "/idbfs/b647bc5784577....".) and I'm not sure how to change that. I'm not using PlayerPrefs so it's not an issue for me.

Also don't know if this workaround is icky for other reasons. But I can now upload new builds as much as I like and my savegame persists!

Hey I know this is super old but I'm using binary files to save. Does this look like roughly what you got working? I'm still having some trouble with this.

public void SaveGameStats()
{
    string saveFileName = "GameStats";
    string savePath = Application.persistentDataPath;
    #if UNITY_WEBGL
        savePath = "idbfs/Skull_Scavenger";
    #endif
    using (FileStream file = File.Create(Path.Combine(savePath, saveFileName)))
    {
        new BinaryFormatter().Serialize(file, curGameStats);
    }
}
(+1)

Hey! I haven't gone anywhere near this stuff in a long time so forgive lack of context but looks like I haaad...

#if UNITY_WEBGL         
    mPersistentDataPath = "idbfs/LittleThings"; 
#else         
    mPersistentDataPath = Application.persistentDataPath; 
#endif

aaaand...

SaveLoadUtility.CreateDirectory(mPersistentDataPath);             
SaveLoadUtility.SaveDataToBlob(appState, mPersistentDataPath + mSaveDataPath);
(+1)

Awesome thanks, the post you made actually helped me figure it out with info from a few other people.

(1 edit)

I'm currently working on a service that will help solve this problem. I'm looking for feedback on what features matter most and what data you want to save.

https://atomicstat.us

Hit me up here, on Twitter (@CharlesPitkin), or on Discord (atomicageindustries)