2025-09-19 08:26:54 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using Managers;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Configs
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 配置管理器,采用单例模式,负责读取、修改、存储应用程序配置。
|
2025-09-28 15:02:57 +08:00
|
|
|
|
/// 继承自 Utils.Singleton<ConfigManager> 和 ILaunchManager。
|
2025-09-19 08:26:54 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class ConfigManager : Utils.Singleton<ConfigManager>, ILaunchManager
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 配置文件名
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const string CONFIG_FILE_NAME = "app_config.json";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-09-28 15:02:57 +08:00
|
|
|
|
/// 完整的配置文件路径,文件位于程序的可执行文件同级目录。
|
|
|
|
|
|
/// 在编辑器中:通常是 Assets 文件夹的父目录。
|
|
|
|
|
|
/// 在构建后:与可执行文件(例如 .exe)在同一目录下。
|
2025-09-19 08:26:54 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly string _configFilePath;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 存储配置数据的字典。使用 JToken 允许存储任意 JSON 类型(基本类型、对象、数组)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Dictionary<string, JToken> _configData;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 私有构造函数,由 Singleton 基类调用。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public ConfigManager()
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
string appDirectory = Path.GetDirectoryName(Application.dataPath);
|
|
|
|
|
|
if (string.IsNullOrEmpty(appDirectory))
|
|
|
|
|
|
{
|
|
|
|
|
|
appDirectory = Application.dataPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_configFilePath = Path.Combine(appDirectory, CONFIG_FILE_NAME);
|
2025-09-19 08:26:54 +08:00
|
|
|
|
_configData = new Dictionary<string, JToken>();
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.Log($"ConfigManager 初始化。配置文件路径: {_configFilePath}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string StepDescription => "载入配置文件";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化配置管理器,加载配置文件。
|
|
|
|
|
|
/// 如果配置文件不存在,将创建一个默认配置。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
LoadConfig();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 清理配置管理器,释放内存中的配置数据。
|
|
|
|
|
|
/// 注意:此方法不会自动保存配置。如果需要在清理前保存,请手动调用 Save()。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
_configData.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从配置文件加载配置数据到内存。
|
|
|
|
|
|
/// 如果文件不存在,则创建默认配置并保存。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void LoadConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (File.Exists(_configFilePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
var json = File.ReadAllText(_configFilePath);
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning($"ConfigManager: 配置文件 '{_configFilePath}' 为空。正在创建默认配置。");
|
|
|
|
|
|
CreateDefaultConfig();
|
|
|
|
|
|
SaveConfig();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 08:26:54 +08:00
|
|
|
|
_configData = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(json);
|
2025-09-28 15:02:57 +08:00
|
|
|
|
if (_configData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning($"ConfigManager: 配置文件 '{_configFilePath}' 包含无效结构。正在创建默认配置。");
|
|
|
|
|
|
CreateDefaultConfig();
|
|
|
|
|
|
SaveConfig();
|
|
|
|
|
|
}
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError(
|
2025-09-28 15:02:57 +08:00
|
|
|
|
$"ConfigManager: 反序列化配置文件 '{_configFilePath}' 失败。正在创建默认配置。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
CreateDefaultConfig();
|
2025-09-28 15:02:57 +08:00
|
|
|
|
SaveConfig();
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (IOException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError(
|
2025-09-28 15:02:57 +08:00
|
|
|
|
$"ConfigManager: 读取配置文件 '{_configFilePath}' 失败。正在创建默认配置。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
CreateDefaultConfig();
|
2025-09-28 15:02:57 +08:00
|
|
|
|
SaveConfig();
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.Log($"ConfigManager: 配置文件 '{_configFilePath}' 未找到。正在创建默认配置。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
CreateDefaultConfig();
|
2025-09-28 15:02:57 +08:00
|
|
|
|
SaveConfig();
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建一个默认的空配置或包含初始值的配置。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CreateDefaultConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
_configData = new Dictionary<string, JToken>();
|
2025-09-28 15:02:57 +08:00
|
|
|
|
SetValue("playerAffiliation", "player");
|
|
|
|
|
|
SetValue("outsideDimension", "DefaultOutsideDimension");
|
|
|
|
|
|
SetValue("insideDimension", "DefaultInsideDimension");
|
|
|
|
|
|
SetValue("baseDimension", "DefaultBaseDimension");
|
|
|
|
|
|
SetValue("configVersion", 1);
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将内存中的配置数据保存到配置文件。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SaveConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
var json = JsonConvert.SerializeObject(_configData, Formatting.Indented);
|
2025-09-19 08:26:54 +08:00
|
|
|
|
File.WriteAllText(_configFilePath, json);
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.Log($"ConfigManager: 配置已保存到 '{_configFilePath}'。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.LogError($"ConfigManager: 序列化配置数据失败。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (IOException ex)
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.LogError($"ConfigManager: 写入配置文件 '{_configFilePath}' 失败。请检查权限。错误: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.UnauthorizedAccessException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"ConfigManager: 写入配置文件 '{_configFilePath}' 权限被拒绝。请以管理员身份运行或更改文件夹权限。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取指定键的配置值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">值的目标类型。</typeparam>
|
|
|
|
|
|
/// <param name="key">配置项的键。</param>
|
|
|
|
|
|
/// <param name="defaultValue">如果键不存在或类型转换失败,返回的默认值。</param>
|
|
|
|
|
|
/// <returns>配置值或默认值。</returns>
|
|
|
|
|
|
public T GetValue<T>(string key, T defaultValue = default(T))
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
if (_configData.TryGetValue(key, out var jToken))
|
2025-09-19 08:26:54 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return jToken.ToObject<T>();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning(
|
2025-09-28 15:02:57 +08:00
|
|
|
|
$"ConfigManager: 键 '{key}' 的值从 '{jToken}' 转换为类型 '{typeof(T).Name}' 失败。" +
|
|
|
|
|
|
$"返回默认值 '{defaultValue}'。错误: {ex.Message}");
|
|
|
|
|
|
return defaultValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.FormatException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning(
|
|
|
|
|
|
$"ConfigManager: 键 '{key}' 的值从 '{jToken}' 转换为类型 '{typeof(T).Name}' 时格式错误。" +
|
|
|
|
|
|
$"返回默认值 '{defaultValue}'。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return defaultValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.LogWarning($"ConfigManager: 未找到键 '{key}'。返回默认值 '{defaultValue}'。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return defaultValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置指定键的配置值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">值的类型。</typeparam>
|
|
|
|
|
|
/// <param name="key">配置项的键。</param>
|
|
|
|
|
|
/// <param name="value">要设置的值。</param>
|
|
|
|
|
|
public void SetValue<T>(string key, T value)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(key))
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.LogError("ConfigManager: 键不能为空。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_configData[key] = JToken.FromObject(value);
|
|
|
|
|
|
Debug.Log(
|
2025-09-28 15:02:57 +08:00
|
|
|
|
$"ConfigManager: 键 '{key}' 的值设置为 '{value}'。请调用 SaveConfig() 持久化更改。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError(
|
2025-09-28 15:02:57 +08:00
|
|
|
|
$"ConfigManager: 键 '{key}' 的值 '{value}' 转换为 JToken 失败。错误: {ex.Message}");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查配置中是否存在某个键。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="key">要检查的键。</param>
|
|
|
|
|
|
/// <returns>如果存在该键,则为 true;否则为 false。</returns>
|
|
|
|
|
|
public bool ContainsKey(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _configData.ContainsKey(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 移除指定键的配置项。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="key">要移除的键。</param>
|
|
|
|
|
|
/// <returns>如果成功移除,则为 true;否则为 false。</returns>
|
|
|
|
|
|
public bool RemoveKey(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_configData.Remove(key))
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.Log($"ConfigManager: 键 '{key}' 已移除。请调用 SaveConfig() 持久化更改。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-28 15:02:57 +08:00
|
|
|
|
Debug.LogWarning($"ConfigManager: 尝试移除不存在的键 '{key}'。");
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 直接获取原始的 JToken 值,适用于处理复杂或嵌套的 JSON 结构。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="key">配置项的键。</param>
|
|
|
|
|
|
/// <returns>对应的 JToken;如果键不存在,则为 null。</returns>
|
|
|
|
|
|
public JToken GetRawJToken(string key)
|
|
|
|
|
|
{
|
2025-09-28 15:02:57 +08:00
|
|
|
|
_configData.TryGetValue(key, out var jToken);
|
2025-09-19 08:26:54 +08:00
|
|
|
|
return jToken;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-28 15:02:57 +08:00
|
|
|
|
}
|