TH1/Unity/Assets/Scripts/TH1_Logic/Config/ConfigManager.cs
2026-05-10 11:52:37 +08:00

411 lines
14 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();
// 老存档兜底:旧版本 GameConfig 没有 SecondaryLanguage 字段,
// JsonUtility 反序列化得到默认值 None此处刷新为 EN。
if (Config.SecondaryLanguage == MultilingualType.None)
{
Config.SecondaryLanguage = MultilingualType.EN;
}
}
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 MultilingualType _secondaryLanguage = MultilingualType.EN;
[SerializeField]
private List<ModLanguageConfig> _modLanguageConfigs = new List<ModLanguageConfig>();
// 老存档迁移标记:第一次进入新版本时,把当前所有本地+订阅 mod 按 targetLanguage 自动挂载到 ModLanguageConfigs之后此标志置 true 不再触发
// 老存档反序列化时此字段不存在 → 默认 false → 自动触发一次迁移
[SerializeField]
private bool _modConfigMigrated;
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;
}
}
// 默认第二语言:玩家在 Setting 里选择 moreLanguage 选项时切换到此语言。
// 老存档反序列化得到 None 时由 GameConfig() 的初值或加载后兜底刷新成 EN。
public MultilingualType SecondaryLanguage
{
get => _secondaryLanguage;
set
{
if (_secondaryLanguage == value) return;
_isChanged = true;
_secondaryLanguage = value;
}
}
/// <summary>
/// Mod 配置是否已从"全自动 apply"模式迁移到"按 ModLanguageConfigs apply"模式
/// 老存档反序列化默认 false → MultilingualManager.Init 检测到 false 时执行一次性迁移
/// </summary>
public bool ModConfigMigrated
{
get => _modConfigMigrated;
set
{
if (_modConfigMigrated == value) return;
_isChanged = true;
_modConfigMigrated = value;
}
}
public GameConfig()
{
_multilingualType = MultilingualType.None;
_musicVolume = 0.5f;
_audioVolume = 0.5f;
_showReminder = true;
_keyMomentEnabled = true;
_bgmContinuousPlay = false;
_secondaryLanguage = MultilingualType.EN;
_modLanguageConfigs = new List<ModLanguageConfig>();
// 全新存档不需要迁移(迁移仅针对老版本"全自动 apply"过的玩家)
_modConfigMigrated = true;
}
// ── 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;
}
}
}
}