TH1/Unity/Assets/Scripts/TH1_Logic/Config/ConfigManager.cs
2026-04-16 14:36:17 +08:00

367 lines
12 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.

/*
* @Author: 白哉
* @Description:
* @Date: 2025年07月24日 星期四 11:07:37
* @Modify:
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ExcelConfig;
using Logic.Config;
using Logic.CrashSight;
using Logic.Multilingual;
using TH1_Logic.Tools;
using UnityEngine;
namespace TH1_Logic.Config
{
public class ConfigManager
{
public static ConfigManager Instance = new ConfigManager();
public GameConfig Config;
public VersionConfig VersionCfg;
private static ColorConfig _colorConfig;
public static ColorConfig ColorConfig
{
get
{
if (_colorConfig == null)
{
_colorConfig = Resources.Load<ColorConfig>("TH1Config/ColorConfig");
}
return _colorConfig;
}
}
public void Init()
{
if (Config == null)
{
string path = Application.persistentDataPath + "/../Config/game_cfg.json";
string backupPath = path + ".bak";
// 尝试读取主文件
Config = TryReadGameConfig(path);
// 主文件损坏,尝试从备份恢复
if (Config == null && File.Exists(backupPath))
{
LogSystem.LogInfo("[ConfigManager] 主文件损坏,尝试从备份恢复...");
Config = TryReadGameConfig(backupPath);
if (Config != null)
{
LogSystem.LogInfo("[ConfigManager] 从备份恢复成功");
try
{
string json = JsonUtility.ToJson(Config);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
FileTools.SafeWriteFile(path, bytes);
}
catch (Exception e)
{
LogSystem.LogError($"[ConfigManager] 恢复写入主文件失败: {e.Message}");
}
}
else
{
LogSystem.LogError("[ConfigManager] 备份文件也已损坏,将重置数据");
}
}
Config ??= new GameConfig();
}
if (!VersionCfg)
{
VersionCfg = Resources.Load<VersionConfig>("Export/VersionConfig");
}
CrashSightAgent.SetAppVersion(VersionCfg.CurVersionInfo.FullVersion);
}
public void InitExcelConfig()
{
// 获取当前程序集中所有实现 IExcelConfig 接口的类
var excelConfigTypes = System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(ExcelConfigBase).IsAssignableFrom(t)
&& t.IsClass
&& !t.IsAbstract);
foreach (var type in excelConfigTypes)
{
try
{
// 创建实例
var instance = (ExcelConfigBase)Activator.CreateInstance(type);
// 这里可以根据需要加载对应的配置数据
// 例如:从 Resources 或其他路径加载 byte[] 数据
byte[] configData = LoadConfigData(type.Name);
// 调用 Init 方法
instance.Init(configData);
instance.AfterInit();
}
catch (Exception ex)
{
LogSystem.LogError($"初始化配置失败: {type.Name}, 错误: {ex.Message}");
}
}
}
private byte[] LoadConfigData(string typeName)
{
string configName = typeName.Replace("Category", "");
TextAsset asset = Resources.Load<TextAsset>($"ExcelConfig/GenerateBytes/{configName}");
if (asset == null || asset.bytes == null || asset.bytes.Length == 0)
{
LogSystem.LogError($"[ConfigManager] 加载配置数据失败: {configName}");
return Array.Empty<byte>();
}
return asset.bytes;
}
private GameConfig TryReadGameConfig(string path)
{
if (!File.Exists(path)) return null;
try
{
string json = File.ReadAllText(path);
if (string.IsNullOrEmpty(json))
{
LogSystem.LogError($"[ConfigManager] 文件为空: {path}");
return null;
}
var config = JsonUtility.FromJson<GameConfig>(json);
if (config == null)
{
LogSystem.LogError($"[ConfigManager] 反序列化结果无效: {path}");
return null;
}
return config;
}
catch (Exception e)
{
LogSystem.LogError($"[ConfigManager] 读取文件失败: {path} | 错误: {e.Message}");
return null;
}
}
private float _lastSaveTime;
private const float SaveInterval = 1f; // 最少间隔1秒写一次
public void Update()
{
if (Config == null || !Config.IsChanged) return;
if (Time.time - _lastSaveTime < SaveInterval) return;
try
{
string json = JsonUtility.ToJson(Config);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
string path = Application.persistentDataPath + "/../Config/game_cfg.json";
FileTools.SafeWriteFile(path, bytes);
Config.MarkSaved();
_lastSaveTime = Time.time;
}
catch (Exception e)
{
LogSystem.LogError($"[ConfigManager] 保存配置失败: {e.Message}");
}
}
}
[Serializable]
public class ModLanguageConfig
{
public MultilingualType Language;
/// <summary>
/// 按优先级排序的 Mod 文件夹路径列表
/// 索引 0 = 最低优先级(最先应用),最后一项 = 最高优先级(最后应用,可覆盖前面的)
/// </summary>
public List<string> ModPaths = new List<string>();
public ModLanguageConfig() { }
public ModLanguageConfig(MultilingualType language) { Language = language; }
}
[Serializable]
public class GameConfig
{
[SerializeField]
private MultilingualType _multilingualType;
[SerializeField]
private float _musicVolume;
[SerializeField]
private float _audioVolume;
[SerializeField]
private bool _showReminder;
[SerializeField]
private bool _keyMomentEnabled;
[SerializeField]
private bool _bgmContinuousPlay;
[SerializeField]
private List<ModLanguageConfig> _modLanguageConfigs = new List<ModLanguageConfig>();
private bool _isChanged;
public bool IsChanged => _isChanged;
public void MarkSaved()
{
_isChanged = false;
}
public MultilingualType MultilingualType
{
get => _multilingualType;
set
{
if (_multilingualType == value) return;
_isChanged = true;
_multilingualType = value;
}
}
public float MusicVolume
{
get => _musicVolume;
set
{
_isChanged = true;
_musicVolume = value;
}
}
public float AudioVolume
{
get => _audioVolume;
set
{
_isChanged = true;
_audioVolume = value;
}
}
public bool BgmContinuousPlay
{
get => _bgmContinuousPlay;
set
{
if (_bgmContinuousPlay == value) return;
_isChanged = true;
_bgmContinuousPlay = value;
}
}
public bool ShowReminder
{
get => _showReminder;
set
{
if (_showReminder == value) return;
_isChanged = true;
_showReminder = value;
}
}
public bool KeyMomentEnabled
{
get => _keyMomentEnabled;
set
{
if (_keyMomentEnabled == value) return;
_isChanged = true;
_keyMomentEnabled = value;
}
}
public GameConfig()
{
_multilingualType = MultilingualType.None;
_musicVolume = 0.5f;
_audioVolume = 0.5f;
_showReminder = true;
_keyMomentEnabled = true;
_bgmContinuousPlay = false;
_modLanguageConfigs = new List<ModLanguageConfig>();
}
// ── Mod 优先级配置接口 ──
/// <summary>获取所有语种的 Mod 配置列表(只读)</summary>
public List<ModLanguageConfig> ModLanguageConfigs => _modLanguageConfigs;
/// <summary>获取指定语种的 Mod 配置,不存在则创建</summary>
public ModLanguageConfig GetOrCreateModConfig(MultilingualType language)
{
var config = _modLanguageConfigs.Find(c => c.Language == language);
if (config != null) return config;
config = new ModLanguageConfig(language);
_modLanguageConfigs.Add(config);
_isChanged = true;
return config;
}
/// <summary>设置指定语种的 Mod 优先级列表(覆盖现有)</summary>
public void SetModsForLanguage(MultilingualType language, List<string> orderedPaths)
{
var config = GetOrCreateModConfig(language);
config.ModPaths = new List<string>(orderedPaths);
_isChanged = true;
}
/// <summary>向指定语种添加一个 Mod加到列表末尾最高优先级</summary>
public void AddModToLanguage(MultilingualType language, string modPath)
{
var config = GetOrCreateModConfig(language);
if (!config.ModPaths.Contains(modPath))
{
config.ModPaths.Add(modPath);
_isChanged = true;
}
}
/// <summary>从指定语种移除一个 Mod</summary>
public void RemoveModFromLanguage(MultilingualType language, string modPath)
{
var config = _modLanguageConfigs.Find(c => c.Language == language);
if (config == null) return;
if (config.ModPaths.Remove(modPath)) _isChanged = true;
}
/// <summary>移动指定语种 Mod 的优先级(向上提升=降低索引,向下降低=增加索引)</summary>
public void MoveModPriority(MultilingualType language, int fromIndex, int toIndex)
{
var config = _modLanguageConfigs.Find(c => c.Language == language);
if (config == null) return;
var paths = config.ModPaths;
if (fromIndex < 0 || fromIndex >= paths.Count) return;
if (toIndex < 0 || toIndex >= paths.Count) return;
var item = paths[fromIndex];
paths.RemoveAt(fromIndex);
paths.Insert(toIndex, item);
_isChanged = true;
}
/// <summary>清除指定语种的所有 Mod 配置</summary>
public void ClearModsForLanguage(MultilingualType language)
{
var config = _modLanguageConfigs.Find(c => c.Language == language);
if (config == null) return;
if (config.ModPaths.Count > 0)
{
config.ModPaths.Clear();
_isChanged = true;
}
}
}
}