Files
2026-03-20 14:00:45 +07:00

281 lines
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace BrewMonster.Scripts
{
public static class DebugCmdHistoryStore
{
private const string ResourcesJsonPathNoExt = "DebugCmdHistory";
private const string PlayerPrefsJsonKey = "DebugCmdHistory_v1";
private const string PlayerPrefsInitedKey = "DebugCmdHistory_inited_v1";
private const int MaxEntries = 200;
[Serializable]
public class Entry
{
public int header;
public int param;
public bool hasParam;
public string describe;
public long lastUsedUtcTicks;
public string KeyString => hasParam ? $"{header}:{param}" : $"{header}:";
}
[Serializable]
private class EntryListWrapper
{
public List<Entry> items = new List<Entry>();
}
public static List<Entry> Load()
{
#if UNITY_EDITOR
return LoadFromEditorJsonFile();
#else
// Runtime: first-run seed from Resources into PlayerPrefs, then keep using PlayerPrefs.
if (!IsInited())
{
var seed = LoadFromResources();
SaveToPlayerPrefs(seed);
SetInited();
return seed;
}
return LoadFromPlayerPrefs();
#endif
}
public static void Save(List<Entry> entries)
{
#if UNITY_EDITOR
SaveToEditorJsonFile(entries);
#else
SaveToPlayerPrefs(entries);
#endif
}
public static bool Upsert(List<Entry> entries, int header, int param, bool hasParam, string describe)
{
if (entries == null)
throw new ArgumentNullException(nameof(entries));
describe = (describe ?? string.Empty).Trim();
int idx = FindIndex(entries, header, param, hasParam);
if (idx >= 0)
{
var e = entries[idx];
bool changed = false;
// Overwrite describe whenever it differs (supports updating from empty -> non-empty).
if (!string.Equals((e.describe ?? string.Empty).Trim(), describe, StringComparison.Ordinal))
{
e.describe = describe;
changed = true;
}
e.lastUsedUtcTicks = DateTime.UtcNow.Ticks;
if (idx != 0)
{
entries.RemoveAt(idx);
entries.Insert(0, e);
changed = true;
}
else if (!changed)
{
// still considered "used"; keep order
}
return changed;
}
else
{
var e = new Entry
{
header = header,
param = param,
hasParam = hasParam,
describe = describe,
lastUsedUtcTicks = DateTime.UtcNow.Ticks
};
entries.Insert(0, e);
if (entries.Count > MaxEntries)
entries.RemoveRange(MaxEntries, entries.Count - MaxEntries);
return true;
}
}
public static void Clear()
{
#if UNITY_EDITOR
try
{
var path = GetEditorJsonAbsolutePath();
if (File.Exists(path))
File.Delete(path);
}
catch (Exception ex)
{
Debug.LogWarning($"[DebugCmdHistoryStore] Clear (editor json) failed: {ex.Message}");
}
#else
PlayerPrefs.DeleteKey(PlayerPrefsJsonKey);
PlayerPrefs.DeleteKey(PlayerPrefsInitedKey);
PlayerPrefs.Save();
#endif
}
public static bool Remove(List<Entry> entries, int header, int param, bool hasParam)
{
if (entries == null)
throw new ArgumentNullException(nameof(entries));
int idx = FindIndex(entries, header, param, hasParam);
if (idx < 0)
return false;
entries.RemoveAt(idx);
return true;
}
private static string GetEditorJsonAbsolutePath()
{
// Keep it under Assets/Resources so it can be included as a TextAsset for runtime seeding.
return Path.Combine(Application.dataPath, "Resources", $"{ResourcesJsonPathNoExt}.json");
}
private static List<Entry> LoadFromEditorJsonFile()
{
try
{
var path = GetEditorJsonAbsolutePath();
if (!File.Exists(path))
return LoadFromResources(); // fallback if file not created yet
var json = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(json))
return new List<Entry>();
var wrapper = JsonUtility.FromJson<EntryListWrapper>(json);
if (wrapper == null || wrapper.items == null)
return new List<Entry>();
wrapper.items.RemoveAll(e => e == null);
return wrapper.items;
}
catch
{
return new List<Entry>();
}
}
private static void SaveToEditorJsonFile(List<Entry> entries)
{
try
{
var wrapper = new EntryListWrapper { items = entries ?? new List<Entry>() };
string json = JsonUtility.ToJson(wrapper, prettyPrint: true);
var path = GetEditorJsonAbsolutePath();
var dir = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
File.WriteAllText(path, json);
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
}
catch (Exception ex)
{
Debug.LogWarning($"[DebugCmdHistoryStore] Save (editor json) failed: {ex.Message}");
}
}
private static List<Entry> LoadFromResources()
{
try
{
var asset = Resources.Load<TextAsset>(ResourcesJsonPathNoExt);
if (asset == null || string.IsNullOrWhiteSpace(asset.text))
return new List<Entry>();
var wrapper = JsonUtility.FromJson<EntryListWrapper>(asset.text);
if (wrapper == null || wrapper.items == null)
return new List<Entry>();
wrapper.items.RemoveAll(e => e == null);
return wrapper.items;
}
catch
{
return new List<Entry>();
}
}
private static List<Entry> LoadFromPlayerPrefs()
{
string json = PlayerPrefs.GetString(PlayerPrefsJsonKey, string.Empty);
if (string.IsNullOrWhiteSpace(json))
return new List<Entry>();
try
{
var wrapper = JsonUtility.FromJson<EntryListWrapper>(json);
if (wrapper == null || wrapper.items == null)
return new List<Entry>();
wrapper.items.RemoveAll(e => e == null);
return wrapper.items;
}
catch
{
return new List<Entry>();
}
}
private static void SaveToPlayerPrefs(List<Entry> entries)
{
try
{
var wrapper = new EntryListWrapper { items = entries ?? new List<Entry>() };
string json = JsonUtility.ToJson(wrapper);
PlayerPrefs.SetString(PlayerPrefsJsonKey, json);
PlayerPrefs.Save();
}
catch (Exception ex)
{
Debug.LogWarning($"[DebugCmdHistoryStore] Save failed: {ex.Message}");
}
}
private static bool IsInited() => PlayerPrefs.GetInt(PlayerPrefsInitedKey, 0) == 1;
private static void SetInited()
{
PlayerPrefs.SetInt(PlayerPrefsInitedKey, 1);
PlayerPrefs.Save();
}
private static int FindIndex(List<Entry> entries, int header, int param, bool hasParam)
{
for (int i = 0; i < entries.Count; i++)
{
var e = entries[i];
if (e == null) continue;
if (e.header != header) continue;
if (e.hasParam != hasParam) continue;
if (hasParam && e.param != param) continue;
return i;
}
return -1;
}
}
}