379 lines
14 KiB
C#
379 lines
14 KiB
C#
#if UNITY_EDITOR
|
||
using System;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using Cysharp.Threading.Tasks;
|
||
using Newtonsoft.Json.Linq;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
namespace ShrinkDataSaver.Editor
|
||
{
|
||
public class ShrinkDataSaverEditorWindow : EditorWindow
|
||
{
|
||
private int _selectedTab;
|
||
private readonly string[] _tabs = { "设置", "存档槽", "工具" };
|
||
private Vector2 _settingsScroll;
|
||
private Vector2 _slotsScroll;
|
||
|
||
private string _setKey = "";
|
||
private string _setValue = "";
|
||
private int _setType; // 0=string, 1=int, 2=float, 3=bool
|
||
private readonly string[] _typeNames = { "字符串", "整数", "浮点数", "布尔值" };
|
||
|
||
private int _saveSlot;
|
||
private string _slotName = "新存档";
|
||
private bool _encrypt;
|
||
private string _encryptKey = "";
|
||
private SaveMeta[] _metaCache;
|
||
|
||
[MenuItem("ShrinkSDK/存档/数据查看器")]
|
||
public static void ShowWindow()
|
||
{
|
||
var w = GetWindow<ShrinkDataSaverEditorWindow>("ShrinkDataSaver");
|
||
w.minSize = new Vector2(460, 500);
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
DrawHeader();
|
||
_selectedTab = GUILayout.Toolbar(_selectedTab, _tabs, EditorStyles.toolbarButton);
|
||
EditorGUILayout.Space(4);
|
||
|
||
switch (_selectedTab)
|
||
{
|
||
case 0: DrawSettingsTab(); break;
|
||
case 1: DrawSaveSlotsTab(); break;
|
||
case 2: DrawToolsTab(); break;
|
||
}
|
||
}
|
||
|
||
private void OnInspectorUpdate() => Repaint();
|
||
|
||
|
||
private void DrawHeader()
|
||
{
|
||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||
GUILayout.Label("ShrinkDataSaver", EditorStyles.boldLabel);
|
||
GUILayout.FlexibleSpace();
|
||
|
||
var color = GUI.color;
|
||
GUI.color = Application.isPlaying ? Color.green : Color.gray;
|
||
GUILayout.Label(Application.isPlaying ? "● 运行中" : "○ 未运行", EditorStyles.miniLabel);
|
||
GUI.color = color;
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
|
||
private void DrawSettingsTab()
|
||
{
|
||
if (!Application.isPlaying)
|
||
{
|
||
EditorGUILayout.HelpBox("设置查看器仅在运行模式下可用。", MessageType.Info);
|
||
DrawSettingsFilePath();
|
||
return;
|
||
}
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("重新加载", EditorStyles.miniButton))
|
||
ShrinkSettings.LoadAsync().Forget();
|
||
if (GUILayout.Button("立即保存", EditorStyles.miniButton))
|
||
ShrinkSettings.SaveAsync().Forget();
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(6);
|
||
|
||
var allSettings = ShrinkSettings.GetAllRaw();
|
||
EditorGUILayout.LabelField($"当前设置项({allSettings.Count} 项)", EditorStyles.boldLabel);
|
||
|
||
if (allSettings.Count == 0)
|
||
{
|
||
EditorGUILayout.HelpBox("暂无设置项。", MessageType.None);
|
||
}
|
||
else
|
||
{
|
||
_settingsScroll = EditorGUILayout.BeginScrollView(_settingsScroll, GUILayout.MaxHeight(240));
|
||
string keyToRemove = null;
|
||
|
||
foreach (var kvp in allSettings.OrderBy(k => k.Key))
|
||
{
|
||
EditorGUILayout.BeginHorizontal("box");
|
||
EditorGUILayout.LabelField(kvp.Key, EditorStyles.boldLabel, GUILayout.Width(140));
|
||
|
||
var valueStr = FormatJToken(kvp.Value);
|
||
EditorGUILayout.LabelField(valueStr, EditorStyles.wordWrappedMiniLabel);
|
||
|
||
var prevColor = GUI.backgroundColor;
|
||
GUI.backgroundColor = new Color(1f, 0.6f, 0.6f);
|
||
if (GUILayout.Button("删除", EditorStyles.miniButton, GUILayout.Width(40)))
|
||
keyToRemove = kvp.Key;
|
||
GUI.backgroundColor = prevColor;
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
if (keyToRemove != null)
|
||
ShrinkSettings.Remove(keyToRemove);
|
||
}
|
||
|
||
EditorGUILayout.Space(8);
|
||
|
||
EditorGUILayout.LabelField("新增 / 修改", EditorStyles.boldLabel);
|
||
_setKey = EditorGUILayout.TextField("键", _setKey);
|
||
_setType = EditorGUILayout.Popup("类型", _setType, _typeNames);
|
||
_setValue = EditorGUILayout.TextField("值", _setValue);
|
||
|
||
EditorGUI.BeginDisabledGroup(string.IsNullOrWhiteSpace(_setKey));
|
||
if (GUILayout.Button("设置", EditorStyles.miniButton))
|
||
SetTypedValue(_setKey, _setValue, _setType);
|
||
EditorGUI.EndDisabledGroup();
|
||
}
|
||
|
||
private static void SetTypedValue(string key, string value, int type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case 0: // string
|
||
ShrinkSettings.Set(key, value);
|
||
break;
|
||
case 1: // int
|
||
if (int.TryParse(value, out var intVal))
|
||
ShrinkSettings.Set(key, intVal);
|
||
else
|
||
Debug.LogWarning($"[ShrinkDataSaver] 无法将 \"{value}\" 解析为整数。");
|
||
break;
|
||
case 2: // float
|
||
if (float.TryParse(value, out var floatVal))
|
||
ShrinkSettings.Set(key, floatVal);
|
||
else
|
||
Debug.LogWarning($"[ShrinkDataSaver] 无法将 \"{value}\" 解析为浮点数。");
|
||
break;
|
||
case 3: // bool
|
||
if (bool.TryParse(value, out var boolVal))
|
||
ShrinkSettings.Set(key, boolVal);
|
||
else
|
||
ShrinkSettings.Set(key, value is "1" or "true" or "True");
|
||
break;
|
||
}
|
||
}
|
||
|
||
private static string FormatJToken(JToken token)
|
||
{
|
||
if (token == null) return "null";
|
||
return token.Type switch
|
||
{
|
||
JTokenType.String => $"\"{token}\"",
|
||
JTokenType.Boolean => token.Value<bool>() ? "true" : "false",
|
||
JTokenType.Array or JTokenType.Object => token.ToString(Newtonsoft.Json.Formatting.None),
|
||
_ => token.ToString()
|
||
};
|
||
}
|
||
|
||
private void DrawSettingsFilePath()
|
||
{
|
||
var cfg = ShrinkDataSaverSettings.Instance;
|
||
var root = string.IsNullOrEmpty(cfg.customSavePath)
|
||
? Application.persistentDataPath
|
||
: cfg.customSavePath;
|
||
EditorGUILayout.LabelField("设置文件路径:", Path.Combine(root, cfg.settingsFileName),
|
||
EditorStyles.wordWrappedMiniLabel);
|
||
|
||
if (GUILayout.Button("打开目录", EditorStyles.miniButton))
|
||
EditorUtility.RevealInFinder(root);
|
||
}
|
||
|
||
|
||
private void DrawSaveSlotsTab()
|
||
{
|
||
if (!Application.isPlaying)
|
||
{
|
||
EditorGUILayout.HelpBox("存档操作仅在运行模式下可用。", MessageType.Info);
|
||
DrawSavesFolderPath();
|
||
return;
|
||
}
|
||
|
||
EditorGUILayout.LabelField("快速操作", EditorStyles.boldLabel);
|
||
|
||
_saveSlot = EditorGUILayout.IntField("槽位索引", _saveSlot);
|
||
_slotName = EditorGUILayout.TextField("存档名称", _slotName);
|
||
_encrypt = EditorGUILayout.Toggle("加密", _encrypt);
|
||
if (_encrypt)
|
||
_encryptKey = EditorGUILayout.TextField("密钥", _encryptKey);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
if (GUILayout.Button("保存", EditorStyles.miniButton))
|
||
{
|
||
ShrinkSave.SaveSlotAsync(_saveSlot, new SaveOptions
|
||
{
|
||
SlotName = _slotName,
|
||
Encrypt = _encrypt,
|
||
EncryptionKey = _encryptKey
|
||
}).Forget();
|
||
RefreshMetaDelayed().Forget();
|
||
}
|
||
|
||
if (GUILayout.Button("加载", EditorStyles.miniButton))
|
||
{
|
||
ShrinkSave.LoadSlotAsync(_saveSlot, _encrypt ? _encryptKey : null).Forget();
|
||
}
|
||
|
||
if (GUILayout.Button("删除", EditorStyles.miniButton))
|
||
{
|
||
if (EditorUtility.DisplayDialog("删除存档",
|
||
$"确认删除槽位 {_saveSlot}?", "删除", "取消"))
|
||
{
|
||
ShrinkSave.DeleteSlotAsync(_saveSlot).Forget();
|
||
RefreshMetaDelayed().Forget();
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(8);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField("所有存档", EditorStyles.boldLabel);
|
||
if (GUILayout.Button("刷新", EditorStyles.miniButton, GUILayout.Width(50)))
|
||
RefreshMeta().Forget();
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
if (_metaCache == null)
|
||
{
|
||
EditorGUILayout.HelpBox("点击「刷新」加载存档列表。", MessageType.None);
|
||
}
|
||
else if (_metaCache.Length == 0)
|
||
{
|
||
EditorGUILayout.HelpBox("暂无存档。", MessageType.None);
|
||
}
|
||
else
|
||
{
|
||
DrawMetaList();
|
||
}
|
||
}
|
||
|
||
private async UniTask RefreshMeta()
|
||
{
|
||
_metaCache = await ShrinkSave.GetAllMetaAsync();
|
||
Repaint();
|
||
}
|
||
|
||
private async UniTaskVoid RefreshMetaDelayed()
|
||
{
|
||
await UniTask.Delay(300);
|
||
await RefreshMeta();
|
||
}
|
||
|
||
private void DrawMetaList()
|
||
{
|
||
_slotsScroll = EditorGUILayout.BeginScrollView(_slotsScroll, GUILayout.MaxHeight(360));
|
||
|
||
foreach (var meta in _metaCache)
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField($"槽位 {meta.SlotIndex} — {meta.SlotName}", EditorStyles.boldLabel);
|
||
GUILayout.FlexibleSpace();
|
||
|
||
if (GUILayout.Button("加载", EditorStyles.miniButton, GUILayout.Width(40)))
|
||
{
|
||
var key = meta.IsEncrypted ? _encryptKey : null;
|
||
ShrinkSave.LoadSlotAsync(meta.SlotIndex, key).Forget();
|
||
}
|
||
|
||
var prevColor = GUI.backgroundColor;
|
||
GUI.backgroundColor = new Color(1f, 0.6f, 0.6f);
|
||
if (GUILayout.Button("删除", EditorStyles.miniButton, GUILayout.Width(40)))
|
||
{
|
||
if (EditorUtility.DisplayDialog("删除存档",
|
||
$"确认删除槽位 {meta.SlotIndex}({meta.SlotName})?", "删除", "取消"))
|
||
{
|
||
ShrinkSave.DeleteSlotAsync(meta.SlotIndex).Forget();
|
||
RefreshMetaDelayed().Forget();
|
||
}
|
||
}
|
||
|
||
GUI.backgroundColor = prevColor;
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.LabelField("最后修改", meta.LastModifiedTime.ToString("yyyy-MM-dd HH:mm:ss"));
|
||
EditorGUILayout.LabelField("游戏时长", TimeSpan.FromSeconds(meta.PlaytimeSeconds).ToString(@"hh\:mm\:ss"));
|
||
EditorGUILayout.LabelField("存档版本", meta.SaveVersion.ToString());
|
||
EditorGUILayout.LabelField("已加密", meta.IsEncrypted ? "是" : "否");
|
||
EditorGUILayout.LabelField("截图", meta.ScreenshotBase64 != null ? "有" : "无");
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(2);
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
private void DrawSavesFolderPath()
|
||
{
|
||
var cfg = ShrinkDataSaverSettings.Instance;
|
||
var root = string.IsNullOrEmpty(cfg.customSavePath)
|
||
? Application.persistentDataPath
|
||
: cfg.customSavePath;
|
||
var saves = Path.Combine(root, "saves");
|
||
|
||
EditorGUILayout.LabelField("存档目录:", saves, EditorStyles.wordWrappedMiniLabel);
|
||
if (GUILayout.Button("打开目录", EditorStyles.miniButton))
|
||
EditorUtility.RevealInFinder(saves);
|
||
}
|
||
|
||
|
||
private void DrawToolsTab()
|
||
{
|
||
EditorGUILayout.LabelField("实用工具", EditorStyles.boldLabel);
|
||
|
||
if (GUILayout.Button("打开持久化数据目录"))
|
||
EditorUtility.RevealInFinder(Application.persistentDataPath);
|
||
|
||
EditorGUILayout.Space(8);
|
||
EditorGUILayout.LabelField("配置资源", EditorStyles.boldLabel);
|
||
var settings = ShrinkDataSaverSettings.Instance;
|
||
if (settings)
|
||
{
|
||
EditorGUILayout.ObjectField("资源文件", settings, typeof(ShrinkDataSaverSettings), false);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox(
|
||
"未找到 ShrinkDataSaverSettings 资源文件。\n" +
|
||
"请通过菜单 Assets → Create → ShrinkDataSaver → Settings 创建。",
|
||
MessageType.Warning);
|
||
}
|
||
|
||
EditorGUILayout.Space(8);
|
||
EditorGUILayout.LabelField("危险操作", EditorStyles.boldLabel);
|
||
|
||
GUI.backgroundColor = new Color(1f, 0.4f, 0.4f);
|
||
if (GUILayout.Button("删除所有存档文件"))
|
||
{
|
||
var cfg = ShrinkDataSaverSettings.Instance;
|
||
var root = string.IsNullOrEmpty(cfg?.customSavePath)
|
||
? Application.persistentDataPath
|
||
: cfg.customSavePath;
|
||
var saves = Path.Combine(root, "saves");
|
||
|
||
if (EditorUtility.DisplayDialog("删除所有存档",
|
||
$"将删除以下目录中的所有内容:\n{saves}\n\n此操作不可撤销!",
|
||
"全部删除", "取消"))
|
||
{
|
||
if (Directory.Exists(saves))
|
||
Directory.Delete(saves, true);
|
||
_metaCache = null;
|
||
Debug.Log("[ShrinkDataSaver] 所有存档文件已删除。");
|
||
}
|
||
}
|
||
|
||
GUI.backgroundColor = Color.white;
|
||
}
|
||
}
|
||
}
|
||
#endif
|