Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Configs/ConfigManager.cs

257 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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