Game UI ready
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEditorInternal;
|
||||
|
||||
|
||||
#pragma warning disable IDE0005
|
||||
using Serilog = Meryel.Serilog;
|
||||
#pragma warning restore IDE0005
|
||||
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
||||
namespace Meryel.UnityCodeAssist.Editor.Preferences
|
||||
{
|
||||
public class PreferenceMonitor
|
||||
{
|
||||
private static readonly Lazy<PreferenceMonitor> _instanceOfPlayerPrefs = new Lazy<PreferenceMonitor>(() => new PreferenceMonitor(true));
|
||||
private static readonly Lazy<PreferenceMonitor> _instanceOfEditorPrefs = new Lazy<PreferenceMonitor>(() => new PreferenceMonitor(false));
|
||||
public static PreferenceMonitor InstanceOfPlayerPrefs => _instanceOfPlayerPrefs.Value;
|
||||
public static PreferenceMonitor InstanceOfEditorPrefs => _instanceOfEditorPrefs.Value;
|
||||
|
||||
//const int Limit = 128;
|
||||
const int Limit = 8192;
|
||||
|
||||
/// <summary>
|
||||
/// PlayerPrefs or EditorPrefs
|
||||
/// </summary>
|
||||
readonly bool isPlayerPrefs;
|
||||
|
||||
#region ErrorValues
|
||||
private readonly int ERROR_VALUE_INT = int.MinValue;
|
||||
private readonly string ERROR_VALUE_STR = "<UCA_ERR_2407201713>";
|
||||
#endregion //ErrorValues
|
||||
|
||||
#pragma warning disable CS0414
|
||||
private static string pathToPrefs = String.Empty;
|
||||
private static string platformPathPrefix = @"~";
|
||||
#pragma warning restore CS0414
|
||||
|
||||
//private string[] userDef;
|
||||
//private string[] unityDef;
|
||||
//private bool showSystemGroup = false;
|
||||
|
||||
private SerializedObject? serializedObject;
|
||||
private ReorderableList? userDefList;
|
||||
private ReorderableList? unityDefList;
|
||||
|
||||
private PreferenceEntryHolder? prefEntryHolder;
|
||||
|
||||
private PreferanceStorageAccessor? entryAccessor;
|
||||
|
||||
|
||||
private bool updateView = false;
|
||||
//private bool monitoring = false;
|
||||
//private bool showLoadingIndicatorOverlay = false;
|
||||
|
||||
|
||||
#if UNITY_EDITOR_LINUX
|
||||
private readonly char[] invalidFilenameChars = { '"', '\\', '*', '/', ':', '<', '>', '?', '|' };
|
||||
#elif UNITY_EDITOR_OSX
|
||||
private readonly char[] invalidFilenameChars = { '$', '%', '&', '\\', '/', ':', '<', '>', '|', '~' };
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
PreferenceMonitor(bool isPlayerPrefs)
|
||||
{
|
||||
this.isPlayerPrefs = isPlayerPrefs;
|
||||
OnEnable();
|
||||
EditorApplication.update += Update;
|
||||
}
|
||||
|
||||
~PreferenceMonitor()
|
||||
{
|
||||
OnDisable();
|
||||
}
|
||||
|
||||
public void Bump()
|
||||
{
|
||||
Serilog.Log.Debug("Bumping preference {IsPlayerPrefs}", isPlayerPrefs);
|
||||
|
||||
RetrieveAndSendKeysAndValues(false);
|
||||
}
|
||||
|
||||
private void RetrieveAndSendKeysAndValues(bool reloadKeys)
|
||||
{
|
||||
string[]? keys = GetKeys(reloadKeys);
|
||||
if (keys == null)
|
||||
return;
|
||||
string[] values = GetKeyValues(reloadKeys, keys, out var stringKeys, out var integerKeys, out var floatKeys, out var booleanKeys);
|
||||
|
||||
if (isPlayerPrefs)
|
||||
MQTTnetInitializer.Publisher?.SendPlayerPrefs(keys, values, stringKeys, integerKeys, floatKeys);
|
||||
else
|
||||
MQTTnetInitializer.Publisher?.SendEditorPrefs(keys, values, stringKeys, integerKeys, floatKeys, booleanKeys);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
#if UNITY_EDITOR_WIN
|
||||
if (isPlayerPrefs)
|
||||
pathToPrefs = @"SOFTWARE\Unity\UnityEditor\" + PlayerSettings.companyName + @"\" + PlayerSettings.productName;
|
||||
else
|
||||
pathToPrefs = @"Software\Unity Technologies\Unity Editor 5.x";
|
||||
|
||||
platformPathPrefix = @"<CurrentUser>";
|
||||
entryAccessor = new WindowsPrefStorage(pathToPrefs);
|
||||
#elif UNITY_EDITOR_OSX
|
||||
if (isPlayerPrefs)
|
||||
pathToPrefs = @"Library/Preferences/com." + MakeValidFileName(PlayerSettings.companyName) + "." + MakeValidFileName(PlayerSettings.productName) + ".plist";
|
||||
else
|
||||
pathToPrefs = @"Library/Preferences/com.unity3d.UnityEditor5.x.plist";
|
||||
|
||||
platformPathPrefix = @"~";
|
||||
entryAccessor = new MacPrefStorage(pathToPrefs);
|
||||
//entryAccessor.StartLoadingDelegate = () => { showLoadingIndicatorOverlay = true; };
|
||||
//entryAccessor.StopLoadingDelegate = () => { showLoadingIndicatorOverlay = false; };
|
||||
#elif UNITY_EDITOR_LINUX
|
||||
if (isPlayerPrefs)
|
||||
pathToPrefs = @".config/unity3d/" + MakeValidFileName(PlayerSettings.companyName) + "/" + MakeValidFileName(PlayerSettings.productName) + "/prefs";
|
||||
else
|
||||
pathToPrefs = @".local/share/unity3d/prefs";
|
||||
|
||||
platformPathPrefix = @"~";
|
||||
entryAccessor = new LinuxPrefStorage(pathToPrefs);
|
||||
#else
|
||||
Serilog.Log.Warning("Undefined Unity Editor platform");
|
||||
pathToPrefs = String.Empty;
|
||||
platformPathPrefix = @"~";
|
||||
entryAccessor = null;
|
||||
#endif
|
||||
|
||||
if (entryAccessor != null)
|
||||
{
|
||||
entryAccessor.PrefEntryChangedDelegate = () => { updateView = true; };
|
||||
entryAccessor.StartMonitoring();
|
||||
}
|
||||
}
|
||||
|
||||
// Handel view updates for monitored changes
|
||||
// Necessary to avoid main thread access issue
|
||||
private void Update()
|
||||
{
|
||||
if (updateView)
|
||||
{
|
||||
updateView = false;
|
||||
//PrepareData();
|
||||
//Repaint();
|
||||
|
||||
Serilog.Log.Debug("Updating preference {IsPlayerPrefs}", isPlayerPrefs);
|
||||
|
||||
RetrieveAndSendKeysAndValues(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
entryAccessor?.StopMonitoring();
|
||||
}
|
||||
|
||||
private void InitReorderedList()
|
||||
{
|
||||
if (prefEntryHolder == null)
|
||||
{
|
||||
var tmp = Resources.FindObjectsOfTypeAll<PreferenceEntryHolder>();
|
||||
if (tmp.Length > 0)
|
||||
{
|
||||
prefEntryHolder = tmp[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
prefEntryHolder = ScriptableObject.CreateInstance<PreferenceEntryHolder>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
serializedObject ??= new SerializedObject(prefEntryHolder);
|
||||
|
||||
userDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("userDefList"), false, true, true, true);
|
||||
unityDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("unityDefList"), false, true, false, false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private string[]? GetKeys(bool reloadKeys)
|
||||
{
|
||||
if (entryAccessor == null)
|
||||
{
|
||||
Serilog.Log.Warning($"{nameof(entryAccessor)} is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
string[] keys = entryAccessor.GetKeys(reloadKeys);
|
||||
|
||||
if (keys.Length > Limit)
|
||||
keys = keys.Where(k => !k.StartsWith("unity.") && !k.StartsWith("UnityGraphicsQuality")).Take(Limit).ToArray();
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
string[]? _cachedKeyValues = null;
|
||||
|
||||
string[]? _cachedStringKeys = null;
|
||||
string[]? _cachedIntegerKeys = null;
|
||||
string[]? _cachedFloatKeys = null;
|
||||
string[]? _cachedBooleanKeys = null;
|
||||
|
||||
private string[] GetKeyValues(bool reloadData, string[] keys,
|
||||
out string[] stringKeys, out string[] integerKeys, out string[] floatKeys, out string[] booleanKeys)
|
||||
{
|
||||
if (!reloadData && _cachedKeyValues != null && _cachedKeyValues.Length == keys.Length)
|
||||
{
|
||||
stringKeys = _cachedStringKeys!;
|
||||
integerKeys = _cachedIntegerKeys!;
|
||||
floatKeys = _cachedFloatKeys!;
|
||||
booleanKeys = _cachedBooleanKeys!;
|
||||
return _cachedKeyValues;
|
||||
}
|
||||
|
||||
string[] values = new string[keys.Length];
|
||||
var stringKeyList = new List<string>();
|
||||
var integerKeyList = new List<string>();
|
||||
var floatKeyList = new List<string>();
|
||||
var boolenKeyList = new List<string>();
|
||||
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var key = keys[i];
|
||||
|
||||
string stringValue;
|
||||
if (isPlayerPrefs)
|
||||
stringValue = PlayerPrefs.GetString(key, ERROR_VALUE_STR);
|
||||
else
|
||||
stringValue = EditorPrefs.GetString(key, ERROR_VALUE_STR);
|
||||
|
||||
if (stringValue != ERROR_VALUE_STR)
|
||||
{
|
||||
values[i] = stringValue;
|
||||
stringKeyList.Add(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
float floatValue;
|
||||
if (isPlayerPrefs)
|
||||
floatValue = PlayerPrefs.GetFloat(key, float.NaN);
|
||||
else
|
||||
floatValue = EditorPrefs.GetFloat(key, float.NaN);
|
||||
|
||||
if (!float.IsNaN(floatValue))
|
||||
{
|
||||
values[i] = floatValue.ToString();
|
||||
floatKeyList.Add(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
int intValue;
|
||||
if (isPlayerPrefs)
|
||||
intValue = PlayerPrefs.GetInt(key, ERROR_VALUE_INT);
|
||||
else
|
||||
intValue = EditorPrefs.GetInt(key, ERROR_VALUE_INT);
|
||||
|
||||
if (intValue != ERROR_VALUE_INT)
|
||||
{
|
||||
values[i] = intValue.ToString();
|
||||
integerKeyList.Add(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool boolValue = false;
|
||||
if (!isPlayerPrefs)
|
||||
{
|
||||
bool boolValueTrue = EditorPrefs.GetBool(key, true);
|
||||
bool boolValueFalse = EditorPrefs.GetBool(key, false);
|
||||
|
||||
boolValue = boolValueFalse;
|
||||
if (boolValueTrue == boolValueFalse)
|
||||
{
|
||||
values[i] = boolValueTrue.ToString();
|
||||
boolenKeyList.Add(key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
values[i] = string.Empty;
|
||||
if (isPlayerPrefs)
|
||||
{
|
||||
// Keys with ? causing problems, just ignore them
|
||||
if (key.Contains("?"))
|
||||
Serilog.Log.Debug("Invalid {PreferenceType} KEY WITH '?', '{Key}' at {Location}, str:{StringValue}, int:{IntegerValue}, float:{FloatValue}, bool:{BooleanValue}",
|
||||
(isPlayerPrefs ? "PlayerPrefs" : "EditorPrefs"), key, nameof(GetKeyValues),
|
||||
stringValue, intValue, floatValue, boolValue);
|
||||
|
||||
else
|
||||
// EditorPrefs gives error for some keys
|
||||
Serilog.Log.Error("Invalid {PreferenceType} '{Key}' at {Location}, str:{StringValue}, int:{IntegerValue}, float:{FloatValue}, bool:{BooleanValue}",
|
||||
(isPlayerPrefs ? "PlayerPrefs" : "EditorPrefs"), key, nameof(GetKeyValues),
|
||||
stringValue, intValue, floatValue, boolValue);
|
||||
}
|
||||
}
|
||||
|
||||
stringKeys = stringKeyList.ToArray();
|
||||
integerKeys = integerKeyList.ToArray();
|
||||
floatKeys = floatKeyList.ToArray();
|
||||
booleanKeys = boolenKeyList.ToArray();
|
||||
|
||||
_cachedKeyValues = values;
|
||||
|
||||
_cachedStringKeys = stringKeys;
|
||||
_cachedIntegerKeys = integerKeys;
|
||||
_cachedFloatKeys = floatKeys;
|
||||
_cachedBooleanKeys = booleanKeys;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private void LoadKeys(out string[]? userDef, out string[]? unityDef, bool reloadKeys)
|
||||
{
|
||||
if(entryAccessor == null)
|
||||
{
|
||||
userDef = null;
|
||||
unityDef = null;
|
||||
return;
|
||||
}
|
||||
|
||||
string[] keys = entryAccessor.GetKeys(reloadKeys);
|
||||
|
||||
//keys.ToList().ForEach( e => { Debug.Log(e); } );
|
||||
|
||||
// Seperate keys int unity defined and user defined
|
||||
Dictionary<bool, List<string>> groups = keys
|
||||
.GroupBy((key) => key.StartsWith("unity.") || key.StartsWith("UnityGraphicsQuality"))
|
||||
.ToDictionary((g) => g.Key, (g) => g.ToList());
|
||||
|
||||
unityDef = (groups.ContainsKey(true)) ? groups[true].ToArray() : new string[0];
|
||||
userDef = (groups.ContainsKey(false)) ? groups[false].ToArray() : new string[0];
|
||||
}
|
||||
|
||||
|
||||
#if (UNITY_EDITOR_LINUX || UNITY_EDITOR_OSX)
|
||||
private string MakeValidFileName(string unsafeFileName)
|
||||
{
|
||||
string normalizedFileName = unsafeFileName.Trim().Normalize(NormalizationForm.FormD);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
// We need to use a TextElementEmumerator in order to support UTF16 characters that may take up more than one char(case 1169358)
|
||||
TextElementEnumerator charEnum = StringInfo.GetTextElementEnumerator(normalizedFileName);
|
||||
while (charEnum.MoveNext())
|
||||
{
|
||||
string c = charEnum.GetTextElement();
|
||||
if (c.Length == 1 && invalidFilenameChars.Contains(c[0]))
|
||||
{
|
||||
stringBuilder.Append('_');
|
||||
continue;
|
||||
}
|
||||
UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c, 0);
|
||||
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user