using System.Collections.Generic;
using UnityEngine;
namespace Ubisoft
{
///
/// Implementation of HtPlayerPrefs that persists data in UnityEngine.PlayerPrefs.
/// All get/set methods in this class need to be called from the main thread, since it's a requirement to access UnityEngine.PlayerPrefs.
///
internal class HtPlayerPrefs_Unity : HtPlayerPrefs_Unity_Base
{
protected const string KEY_STORED_KEYS = "__storedKeys";
struct KeyMetadata
{
internal IHtPlayerPrefs.EType Type { get; }
internal string TypeAsKey { get { return ETypeToKey(Type); } }
internal string FullKey { get; }
internal KeyMetadata(IHtPlayerPrefs.EType type, string fullKey)
{
Type = type;
FullKey = fullKey;
}
}
private string StoredKeysFullKey { get; }
private Dictionary StoredKeys { get; set; }
///
/// Since Unity PlayerPrefs doesn't support long values we need to stored them as strings.
/// Long keys are stored in memory to avoid memory allocations every time they're retrieved.
///
private Dictionary LongValues { get; set; }
///
/// Create an HtPlayerPrefs_Unity object to store data in the channel passed in.
/// By default AutomaticSave is enabled. Feel free to change it if you're concerned about its performance impact.
///
/// Name of the channel to use to store data saved through this object.
/// An HtLogger.Channel object to use to log data to.
/// If no object is passed in then a default one called 'HT_PlayerPrefs('channel')' is created.
/// When true logs are printed out. Use it only for debug purposes.
/// Make sure that Trace is the minimum log level set in your Logger configuration.
internal HtPlayerPrefs_Unity(string channel, HtLogger.Channel logger = null, bool isVerbose = false) : base(channel, logger, isVerbose)
{
StoredKeys = new Dictionary();
StoredKeysFullKey = GetFullKey(KEY_STORED_KEYS);
LoadStoredKeys();
}
public override bool GetBool(string key, bool defaultValue = false)
{
return IntToBool(PlayerPrefs.GetInt(GetFullKey(key), BoolToInt(defaultValue)));
}
public override float GetFloat(string key, float defaultValue = 0f)
{
return PlayerPrefs.GetFloat(GetFullKey(key), defaultValue);
}
public override int GetInt(string key, int defaultValue = 0)
{
return PlayerPrefs.GetInt(GetFullKey(key), defaultValue);
}
public override long GetLong(string key, long defaultValue = 0)
{
if (LongValues == null)
{
LongValues = new Dictionary();
}
if (!LongValues.TryGetValue(key, out long returnValue))
{
string fullKey = GetFullKey(key);
if (PlayerPrefs.HasKey(fullKey))
{
string rawValue = PlayerPrefs.GetString(fullKey);
StringToLong(key, rawValue, out returnValue);
}
}
return returnValue;
}
public override string GetString(string key, string defaultValue = "")
{
return PlayerPrefs.GetString(GetFullKey(key), defaultValue);
}
public override IHtPlayerPrefs.EType GetType(string key)
{
return StoredKeys.ContainsKey(key) ? StoredKeys[key].Type : IHtPlayerPrefs.EType.None;
}
public override bool HasKey(string key)
{
return PlayerPrefs.HasKey(GetFullKey(key));
}
protected override void ExtendedDeleteAll()
{
foreach (KeyValuePair pair in StoredKeys)
{
PlayerPrefs.DeleteKey(GetFullKey(pair.Key));
}
PlayerPrefs.DeleteKey(StoredKeysFullKey);
StoredKeys.Clear();
NeedsToSave = true;
}
protected override void ExtendedDeleteKey(string key)
{
if (StoredKeys.ContainsKey(key))
{
PlayerPrefs.DeleteKey(GetFullKey(key));
_ = StoredKeys.Remove(key);
UpdateRawStoredKeys();
NeedsToSave = true;
}
}
protected override void ExtendedSetBool(string key, bool value)
{
ExtendedSetInt(key, BoolToInt(value));
NeedsToSave = true;
}
protected override void ExtendedSetFloat(string key, float value)
{
PlayerPrefs.SetFloat(GetFullKey(key), value);
AddStoredKey(key, IHtPlayerPrefs.EType.Float);
NeedsToSave = true;
}
protected override void ExtendedSetInt(string key, int value)
{
PlayerPrefs.SetInt(GetFullKey(key), value);
AddStoredKey(key, IHtPlayerPrefs.EType.Int);
NeedsToSave = true;
}
protected override void ExtendedSetLong(string key, long value)
{
if (LongValues == null)
{
LongValues = new Dictionary();
}
if (LongValues.ContainsKey(key))
{
LongValues[key] = value;
}
else
{
LongValues.Add(key, value);
}
// Stored as string because Unity PlayerPrefs doesn't support long type
PlayerPrefs.SetString(GetFullKey(key), LongToString(value));
AddStoredKey(key, IHtPlayerPrefs.EType.Long);
NeedsToSave = true;
}
protected override void ExtendedSetString(string key, string value)
{
PlayerPrefs.SetString(GetFullKey(key), value);
AddStoredKey(key, IHtPlayerPrefs.EType.String);
NeedsToSave = true;
}
public override string ToString()
{
return $"StoredKeys: {RawStoredKeys}\nContent: {ContentAsString()}";
}
private string ContentAsString()
{
string returnValue = "";
string valueAsString;
foreach (KeyValuePair pair in StoredKeys)
{
if (HasKey(pair.Key))
{
if (pair.Value.Type != IHtPlayerPrefs.EType.None)
{
valueAsString = null;
switch (pair.Value.Type)
{
case IHtPlayerPrefs.EType.Bool:
valueAsString = GetBool(pair.Key).ToString();
break;
case IHtPlayerPrefs.EType.Int:
valueAsString = GetInt(pair.Key).ToString();
break;
case IHtPlayerPrefs.EType.Long:
valueAsString = GetLong(pair.Key).ToString();
break;
case IHtPlayerPrefs.EType.Float:
valueAsString = GetFloat(pair.Key).ToString();
break;
case IHtPlayerPrefs.EType.String:
valueAsString = GetString(pair.Key);
break;
}
if (valueAsString != null)
{
if (!string.IsNullOrEmpty(returnValue))
{
returnValue += $",";
}
returnValue += $"{pair.Key}:{valueAsString}";
}
}
}
}
return returnValue;
}
private void LoadStoredKeys()
{
string key;
string rawStoredKeys = RawStoredKeys;
if (!string.IsNullOrEmpty(rawStoredKeys))
{
bool needsToCorrectRawStoredKeys = false;
string[] keys = rawStoredKeys.Split(STORED_KEYS_KEY_SEPARATOR);
string[] tokens;
int count = keys.Length;
for (int i = 0; i < count; ++i)
{
key = keys[i].Trim();
if (!string.IsNullOrEmpty(key))
{
tokens = key.Split(STORED_KEYS_TOKEN_SEPARATOR);
if (tokens.Length < 2)
{
Logger.LogError($"Malformed raw stored key {Debug.FormatTextInUserContext(key)}");
needsToCorrectRawStoredKeys = true;
}
else if (StoredKeys.ContainsKey(tokens[0]))
{
Logger.LogError($"Duplicate raw stored key {Debug.FormatTextInUserContext(key)} in {Debug.FormatTextInUserContext(rawStoredKeys)}");
needsToCorrectRawStoredKeys = true;
}
else
{
IHtPlayerPrefs.EType type = KeyToEType(tokens[1]);
if (type == IHtPlayerPrefs.EType.None)
{
Logger.LogError($"Unsupported type {Debug.FormatTextInUserContext(tokens[1])} in raw stored key {Debug.FormatTextInUserContext(key)}");
needsToCorrectRawStoredKeys = true;
}
else
{
StoredKeys.Add(tokens[0], new KeyMetadata(type, GetFullKey(tokens[0])));
}
}
}
else
{
needsToCorrectRawStoredKeys = true;
Logger.LogError($"Empty raw stored key in {Debug.FormatTextInUserContext(rawStoredKeys)}");
}
}
if (needsToCorrectRawStoredKeys)
{
UpdateRawStoredKeys();
Logger.LogWarning($"Correct raw stored keys from: {Debug.FormatTextInUserContext(rawStoredKeys)} to {Debug.FormatTextInUserContext(RawStoredKeys)}");
}
}
}
private void AddStoredKey(string key, IHtPlayerPrefs.EType type)
{
if (!StoredKeys.ContainsKey(key) && type != IHtPlayerPrefs.EType.None)
{
StoredKeys.Add(key, new KeyMetadata(type, GetFullKey(key)));
RawStoredKeys = AddRawStoredKey(key, type, RawStoredKeys);
}
}
private string RawStoredKeys
{
get
{
return GetString(KEY_STORED_KEYS);
}
set
{
PlayerPrefs.SetString(StoredKeysFullKey, value);
NeedsToSave = true;
}
}
private void UpdateRawStoredKeys()
{
string value = "";
foreach (KeyValuePair pair in StoredKeys)
{
value = AddRawStoredKey(pair.Key, pair.Value.Type, value);
}
RawStoredKeys = value;
}
private string GetFullKey(string key)
{
return StoredKeys.ContainsKey(key) ? StoredKeys[key].FullKey : $"{Channel}.{key}";
}
}
}