We released our game on several platforms, including WebGL. This game saves some local stuff (app language, local users...) into the PlayerPrefs, using the browser's IndexedDB API.
Though we noticed after uploading a new version of the app on our server, the data in the PlayerPrefs is gone.
Is there any way of keeping these data, after a new upload of the game?
I've read taht WebGL can't write to disk, so reading/saving a file on disk isn't possible.
Some topics about this problem mention the use of cookies, but couldn't find any decent documentation on how to use this.
Is there a decent solution to fix this?
Kind regards,
Carsten
yes Unity is saving all data (including PlayerPrefs) to IndexedDB.
You can see these files i.e. in Chrome DevTools under "Application":
Storage-> IndexedDB -> /idbfs {{YOUR SERVER}} -> FILE_DATA
We you make a new build with a different Unity-Version or a different machine the hash-value changes. So the saved files from the old build can not be found by Applicaton.persistentDataPath
A workaround can be using the LocalStorage from your browser if you are only saving strings or numbers.
You can write some javascript functions (for example in <script> tags in your template file) to store your text data there - it very similar to the PlayerPrefs:
var saveData = function(yourkey, yourdata){
localStorage.setItem(yourkey, yourdata);
}
to save values. And
var loadData = function(yourkey){
localStorage.getItem(yourkey);
}
to get them.
Create a plugin.jslib file in Unity Editor and call these functions:
mergeInto(LibraryManager.library, {
GetData: function(yourkey){
if(typeof loadData !== 'undefined'){
loadData(Pointer_stringify(yourkey));
} else {
console.log("Javacript function not available...");
}
},
SetData: function(yourkey, yourdata){
if(typeof saveData !== 'undefined'){
saveData(Pointer_stringify(yourkey), Pointer_stringify(yourdata));
} else {
console.log("Javacript function not available...");
}
}
});
Now you can call them from Script as metioned here
Related
Project Requirement: Music player app which will download audio files, encrypt and save them. The audio files should be playable in the app only. No other app should be able to play the files. Nor the user should be able to copy the files.
Approach: I don't want the entire decryted audio file to exist at any moment. So I want to encrypt the audio file as soon as it is downloaded. Then when the file is to be played, I want it to be decrypted chunk-by-chunk and played. I believe this can be achieved by using stream. As far as I searched, a package named "just_audio" can play the audio from stream source.
Problem: I cannot find any encryption package for Flutter/Dart which will output the decrypted data in the form of a stream. This is the first time I am trying to implement encryption/decryption, so my knowledge is very poor in this regard.
Notes:
The encryption does not need to be heavy. A typical user not being able to copy the files and play them elsewhere would suffice.
Audio files are going to be large, some of them even hours in length.
I need all the usual functions of a music player (e.g. albums, playlists, progress bars with seeking function, etc.)
Options:
It will be best if there is a package which can do what I need, off the shelf.
To find a basic package and then modifying it into doing what is needed.
Some radically different solution, which takes entirely different path but provides all the solutions.
Firstly, to encrypt or decrypt data, have a look at https://pub.dev/packages/cryptography or https://pub.dev/packages/encrypt or something like that.
Secondly, since you want seeking, it may not be an optimal solution to use streams - the "Stream" abstraction is more like a sequence of data, instead of arbitrarily jumping (seeking) here and there. Instead, divide whole audio (say 1hour) into chunks (say 1minute), and upload/encrypt/decrypt/download each chunk separately and as a whole without using streams. If your chunk is small enough, download/decrypt a chunk will be fast and you do not stuck the UI. If the decrypting is still too slow, have a look at isolates, which are "threads" in Flutter. Run decrypt in a separate isolate then your UI will be smooth.
I need all the usual functions of a music player (e.g. albums, playlists, progress bars with seeking function, etc.)
Seeking is implemented above. For albums/playlists, you may modify any normal audio players in flutter to implement that, or directly implement by your own. it is more like just some UI things, no special things, and anyone familiar with Flutter can write it, no worry.
if you're open to a 3rd package, don't reinvent the wheel..
try this here https://morioh.com/p/34a06006b299 with various CipherStream Options
If you can forego stream encryption and do it after you have the file then try this package, Credit: I used the sample from this answer by Hoaea Varghese
With AES all you need is the path to the file, and you can encrypt files or albums with something as simple as
encrypted_file_path = EncryptData.encrypt_file('your/file/path');
With the code below
import 'dart:io';
import 'package:aes_crypt/aes_crypt.dart';
class EncryptData {
static String encrypt_file(String path) {
AesCrypt crypt = AesCrypt();
crypt.setOverwriteMode(AesCryptOwMode.on);
crypt.setPassword('my cool password');
String encFilepath;
try {
encFilepath = crypt.encryptFileSync(path);
print('The encryption has been completed successfully.');
print('Encrypted file: $encFilepath');
} catch (e) {
if (e.type == AesCryptExceptionType.destFileExists) {
print('The encryption has been completed unsuccessfully.');
print(e.message);
}
else{
return 'ERROR';
}
}
return encFilepath;
}
static String decrypt_file(String path) {
AesCrypt crypt = AesCrypt();
crypt.setOverwriteMode(AesCryptOwMode.on);
crypt.setPassword('my cool password');
String decFilepath;
try {
decFilepath = crypt.decryptFileSync(path);
print('The decryption has been completed successfully.');
print('Decrypted file 1: $decFilepath');
print('File content: ' + File(decFilepath).path);
} catch (e) {
if (e.type == AesCryptExceptionType.destFileExists) {
print('The decryption has been completed unsuccessfully.');
print(e.message);
}
else{
return 'ERROR';
}
}
return decFilepath;
}
}
The file needs to be read and write later. On Android I used Room & RoomAssetHelper.
I understand the basic logic that I need to include it in the project assets and then copy it to somewhere accessible for the app at first start. But I want to avoid writing these things manually and risking making an error (I am not too confident with reading files & DBs).
All of the answers that I find are from people giving quick & dirty advice on how to manually code the logic for this. I would like to do it on a clean & professional level.
Is there a library that would do most of the "risky" work for me?
(Meaning import & copy the .sqlite file, so I can start using it in my code)
I found GRDB.swift, but I cannot figure out if it supports prepopulated files.
Please stop looking for a magical library that will do all of this for you automatically.
What you need to do yourself without any SQLite library?
Add your prepopulated database.sqlite as an asset to your project.
When the app launches, check if the database.sqlite file is present at the expected location (inside your app's documents directory for example). You can check this using FileManager APIs.
If the file exists at the expected path, you are fine, no need to copy any file.
If the database.sqlite file does not exist at the expected path, you need to copy your database.sqlite file at the path using FileManager APIs.
CAUTION :
Be aware that in this step, you may encounter an error while copying the file. This should not happen for most cases. In rare caes that it does happen, you should adjust your app accordingly - indicate to user somehow that initialization failed, free some space on your phone, restart app etc.
Steps 2-4 need to be checked on every app launch - put this logic somewhere close your app startup process. If all of above instructions are followed and you succeed either via step 3 OR 4, you now have the database.sqlite file where you want it to be.
Where the SQLite library comes in?
At this point, you can use any library that suits your purpose and you feel comfortable with.
As you mentioned GRDB.swift, it allows you to specify a custom path for your database file. Copy-pasting the current version minimal setup code here for reference.
import GRDB
// 1. Open a database connection
let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
// 2. Define the database schema
try dbQueue.write { db in
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text).notNull()
t.column("score", .integer).notNull()
}
}
// 3. Define a record type
struct Player: Codable, FetchableRecord, PersistableRecord {
var id: Int64
var name: String
var score: Int
}
// 4. Access the database
try dbQueue.write { db in
try Player(id: 1, name: "Arthur", score: 100).insert(db)
try Player(id: 2, name: "Barbara", score: 1000).insert(db)
}
let players: [Player] = try dbQueue.read { db in
try Player.fetchAll(db)
}
Okay, so I've been struggling around and searching for a while, saw a lot of different posts, but I did not find answer to my question.
My problem:
I have a scene in Unity with nothing in it, everything is created proceduraly and randomly on game start, and of course I want the player to be able to save his progress. I have found ways of saving progress in Unity, but everything was about writing a script for each class or object I want to save, but these seem to me inefficient, since in my game, there are randomly generated houses and buildings (which would be relatively easy to save), but there can also be objects placed inside these buildings and so on. Also later I plan on adding characters, which also need to be saved (like where they are, what are they holding and such). And as I mentioned, writing a save and load script for each object seems inefficient to me, since I'm used to Java's serializtaion, where I just write my Main Object containing all data to a file, so I'm looking for some easier ways to do so. Possibly a way to save entire scene state and then on loading just load the scene instead of generating it from scratch.
My Question:
Is there a way to save whole scene with all objects and information about them and then load it?
Thank you in advance!
There's no built-in method by which you can "save a scene" during runtime and then reload it later. In a Unity build, scenes are stored in a non-editable format, meaning that whenever you load a scene it will load up with the same format as it was built. This is a good thing, because you don't want to edit the contents of your build in a deployed game.
Now, that doesn't mean that a scene can't contain logic to configure itself differently. In fact, it sounds like that's what you're doing. The goal instead is to store the contents into a save file.
Consider switching your mentality from "I want to load a scene that generates random gameplay" to "I want to load a scene that configures itself based on a file." This is a layer of abstraction that gives greater control over what happens when you load your scene.
I would suggest creating a JSON configuration file that stores your important information, something like this might do:
{
"house_locations": [
{
"position": "(0, 0, 0)",
"objects": []
},
{
"position": "(10, 10, 10)",
"objects": []
}
],
"characters": [
{
"position": "(0, 0, 0)",
"inventory": [
{
"item_name": "knife"
},
{
"item_name": "shovel"
}
]
}
]
}
This is just a simple example, as you'll have to add the important data you want to represent your game.
Next, all you have to do when you want to start your game is to do one of the following things:
Are you starting a new game? => Generate a random configuration file, then use that to populate your scene.
Are you loading a saved game? => Use the saved configuration file to populate your scene.
You'll need some kind of WorldBuilder script to handle this. In your scene, you can have something like the following:
public class WorldBuilder : MonoBehaviour
{
// This is the actual contents of the world, represented as a JSON string.
private string _json = "";
public void BuildWorld(string configFilePath)
{
_json = LoadConfiguration(configFilePath);
BuildWorld(_json);
}
public void GenerateWorld()
{
_json = GenerateConfiguration();
BuildWorld(_json);
}
public void SaveWorld(string targetFilePath)
{
// Save the contents of _json out to a file so that it can be loaded
// up again later.
}
private string LoadConfiguration(string configFilePath)
{
// Load the actual file and return the file contents, which is a JSON string.
}
private void BuildWorld(string json)
{
// Actually build the world using the supplied JSON.
}
private string GenerateConfiguration()
{
// Return a randomly generated configuration file.
}
}
This approach separates the problem of saving the contents of the scene and generating the contents of the scene, making the code easier to write and maintain.
I do not know how to build a scene save patternor principles.
But there is a plugin which save scene in play mode, you may configure it according to your project.
https://assetstore.unity.com/packages/tools/utilities/autosaver-don-t-waste-time-anymore-54247
I would name 'LoadConfiguration' to 'LoadWorld'. Actually, I'd name it 'WorldSave' and 'WorldLoad' so it all matches up alphabetically.
Also, have loading be asynchronous:
public IEnumerator WorldLoad(string localWorldFilePath)
{
string filePath = Application.persistentDataPath + "/" + localWorldFilePath + ".json");
string lines = null;
//Loading the level loop:
using (SteamReader streamReader = new StreamReader(filePath,Encoding.UTF8))
{
lines += streamReader.ReadToEnd();
if (UnityEngine.Random.Range(0f,20f) > 15f) //< - probably a fancier way of doing this part)
yield return new WaitForEndOfFrame();
}
//Okay, we're done loading so return this:
yield return lines;
}
In fact, have saving be asynchronous too, using StreamWriter and make sure it's also encoded in UTF8, you can't just use File.Save and File.Load in other words.
Another idea, encrypt your files if you don't want modding (but people will just decrypt it anyway).
Start the coroutine somewhere else, the lower you make that '15f' the more it'll lock up your game, the higher you make it the less it will but the slower it'll go. There's a way to do it on a separate thread or something but I'm not an advanced programmer.
worldBuilder.StartCoroutine(WorldLoad);
TIP 1:
Ideally you wanna create your own scripting language through a console, if the idea of real-time custom scene file loading and saving is that you can create and edit your levels while the actual game is running so that you're bypassing using Unity as a level editor for example.
In other words you could press ~ and bring a console up and type 'WorldLoad(worldNameGoesHere)' and it'll load the world in your games built folders.
There's also a way using an editor script you can actually hook in and override Unity's standard 'Scene Save', in case you wanna do that also.
TIP 2:
Also make sure you encode in UTF8 format like above, saving and loading so you don't have it become a pain later when you want to implement asian languages like Japanese, Chinese, etc...
Currently I am saving game progress in a file in JSON format. Each time player completes the level a new entry will be added to JSON file as follow "level_N":{"score":234,"points":22},
this file will be saved at the end of Level complete. Consider scenario in which player reaches level 2345 or so, In that case saving to the file on level complete takes considerably longer time in some mobile devices. how to manage saving such a large amount of data ? do I have to use some other formats ? or do i have to save each level detail in separate file ?
PlayerPrefs is the easiest way to save data. It’s designed to handle basic data types (int, string, float) and works like a dictionary, so you can simply store JSON string as key-value pairs. And there is no size limit on iOS or Android (in webplayer it's limited to 1MB).
// read
PlayerPrefs.GetString(string key, string value);
// write
PlayerPrefs.SetString(string key, string value);
// load and update
const saveKey = "level_N";
Private void SaveProgress()
{
string saveValue = "your JSON string";
string loadValue = PlayerPrefs.GetString(saveKey);
if (!saveValue.Equals(loadValue))
{
PlayerPrefs.SetString(saveKey, saveValue);
PlayerPrefs.Save();
}
}
User data will be automatically written to disk during OnApplicationQuit(), but you may want to use PlayerPrefs.Save() in case your game crashes.
Having said that, saving a large amount of data to PlayerPrefs on mobile devices might be slow. So if you want to improve your game performance or need more space, you can use Application.persistentDataPath to save data to a public directory on the device. Similar to PlayerPrefs, data is not cleared when app is updated.
FileStream file = File.Open(Application.persistentDataPath + "/gameInfo.dat", FileMode.Open);
You can use PlayerPrefs class to save to save the local data.
In your case, you can trun your json to string then call PlayerPrefs.SetString to save/PlayerPrefs.GetString to get.
PlayerPrefs is always a good choice for local data managing but if you planning to add online features to your game, I recommend you to take a look at Firebase Realtime Database for sync json data with offline support. And let the firebase manage performance issues on mobile devices. Here you can see how to integrate Firebase Database to your Unity project easily.
http://docs.meteor.com/#meteor_startup
Concerning Meteor.startup(func)
On a client, the function will run as soon as the DOM is ready.
At what point is the "DOM ready"?
The reason why I ask is because you can have a Meteor template that uses many other templates (ie. other DOM elements) inside of it and have many other things loading at different times depending on how long those things take to load (ie. more DOM elements).
In the past I've loaded Javascript files on Meteor.startup but they were still loaded too early because the entire DOM actually had not been loaded yet.
Meteor.startup( function() {
//load JS files now
});
So what does it mean by "DOM ready?" It most definitely does not mean "When the DOM is loaded in its entirety."
Meteor.startup actually runs when all the files have completely downloaded from the server (javascript files). If you place your code to run at startup without putting it in a Meteor.startup it may not run because it would run where the JS/html has not been fully downloaded yet
This is when the 'DOM is ready', but not necessarily when your HTML is rendered, because this (the HTML) renders when the DOM is ready too.
If you're looking for something that runs after both the DOM is ready and after your page's html is ready look for the template's .rendered callback - http://docs.meteor.com/#template_rendered
The confusion may come from the concept of $(document).ready in JQuery, but this applies because the page is already rendered on the server side so you can assume its also rendered on the client (since its downloaded that way). In Meteor pages are rendered on the client so there is this slight difference.
Here's what the .startup method actually does on the client:
Meteor.startup = function (cb) {
var doScroll = !document.addEventListener &&
document.documentElement.doScroll;
if (!doScroll || window !== top) {
if (loaded)
cb();
else
queue.push(cb);
} else {
try { doScroll('left'); }
catch (e) {
setTimeout(function() { Meteor.startup(cb); }, 50);
return;
};
cb();
}
};