using System; using System.Collections.Generic; using UnityEngine; using Cysharp.Threading.Tasks; using System.Reflection; using System.Linq; #if UNITY_EDITOR using UnityEditor; #endif public class ConfigManager : ManagerBase { public bool isLoadFinished { get; private set; } private List preInitConfig = new List() { typeof (FuncConfigConfig), }; private float loadingProgress = 0f; public override async UniTask Init() { await InitConfigs(); } public virtual async UniTask InitConfigs() { // 加载配置文件 await LoadConfigs(); } public async UniTask PreInit() { List uniTasks = new List(); foreach (var type in preInitConfig) { uniTasks.Add(LoadConfigByTypeAsync(type)); } await UniTask.WhenAll(uniTasks); } protected async UniTask LoadConfigs() { loadingProgress = 0f; isLoadFinished = false; // 自动生成:收集所有配置类型(此段将被 ConfigGenerater.Generate 自动覆盖) // 自动生成:收集所有配置类型 // 自动生成:收集所有配置类型 // 自动生成:收集所有配置类型 HashSet configTypes = new HashSet() { typeof(ActLunhuidianTypeConfig), typeof(ADAwardConfig), typeof(AppointItemConfig), typeof(AudioConfig), typeof(BattleMapConfig), typeof(BeautyConfig), typeof(BeautyQualityLVConfig), typeof(BeautySkinConfig), typeof(ChatBubbleBoxConfig), typeof(ChestsAwardConfig), typeof(ChestsConfig), typeof(CTGConfig), typeof(CTGSelectItemConfig), typeof(DailyLivenessRewardConfig), typeof(DailyTaskConfig), typeof(DamageNumConfig), typeof(DirtyNameConfig), typeof(DirtyWordConfig), typeof(DungeonConfig), typeof(DungeonOpenTimeConfig), typeof(EffectConfig), typeof(EmojiPackConfig), typeof(FaceConfig), typeof(FamilyConfig), typeof(FamilyDonateConfig), typeof(FamilyEmblemConfig), typeof(FBDJGEffectConfig), typeof(FBDJGLevelConfig), typeof(FBDJGQuickConfig), typeof(FightPowerRatioConfig), typeof(FirstChargeConfig), typeof(FirstGoldConfig), typeof(FrameAnimationConfig), typeof(FuncConfigConfig), typeof(FuncOpenLVConfig), typeof(FunctionTeamSetConfig), typeof(GetItemWaysConfig), typeof(GmCmdConfig), typeof(GoldRushCampConfig), typeof(GoldRushItemConfig), typeof(GoldRushWorkerConfig), typeof(GubaoConfig), typeof(GubaoLVAttrConfig), typeof(GubaoLVConfig), typeof(GubaoResonanceAttrConfig), typeof(GubaoResonanceConfig), typeof(GubaoStarConfig), typeof(GuideConfig), typeof(HeroAwakeConfig), typeof(HeroBreakConfig), typeof(HeroConfig), typeof(HeroFatesConfig), typeof(HeroFatesQualityLVConfig), typeof(HeroFetterConfig), typeof(HeroLineupHaloConfig), typeof(HeroQualityAwakeConfig), typeof(HeroQualityBreakConfig), typeof(HeroQualityConfig), typeof(HeroQualityLVConfig), typeof(HeroSkinConfig), typeof(HeroTalentConfig), typeof(HorseClassConfig), typeof(HorseSkinConfig), typeof(IconConfig), typeof(ItemCompoundConfig), typeof(ItemConfig), typeof(KickOutReasonConfig), typeof(LanguageConfig), typeof(LineupRecommendConfig), typeof(LLMJConfig), typeof(MailConfig), typeof(MainChapterConfig), typeof(MainLevelConfig), typeof(MGGanwuLVConfig), typeof(MGGuayuQualityConfig), typeof(MGGuayuTypeConfig), typeof(MGLingyingQualityConfig), typeof(ModelConfig), typeof(NPCConfig), typeof(NPCExConfig), typeof(NPCLineupConfig), typeof(OPConfigConfig), typeof(OrderInfoConfig), typeof(PlayerAttrConfig), typeof(PlayerFaceConfig), typeof(PlayerFacePicConfig), typeof(PlayerLVConfig), typeof(PlayerPropertyConfig), typeof(PresetUnlockConfig), typeof(PriorBundleConfig), typeof(RandomNameConfig), typeof(RealmConfig), typeof(RealmLVUPTaskConfig), typeof(RichTextMsgReplaceConfig), typeof(RuleConfig), typeof(SignInConfig), typeof(SkillConfig), typeof(SkillSkinConfig), typeof(StoreConfig), typeof(SuccessConfig), typeof(SysInfoConfig), typeof(TaskConfig), typeof(TianziConfig), typeof(TimingGiftConfig), typeof(TimingGiftTypeConfig), typeof(TitleConfig), typeof(TitleStarUpConfig), typeof(TravelEventConfig), typeof(TravelSceneryConfig), typeof(TreasureCntAwardConfig), typeof(TreasureItemLibConfig), typeof(TreasureSetConfig), typeof(TreeLVConfig), typeof(WindowSearchConfig), typeof(XBGetItemConfig), typeof(ZhanlingConfig) }; int iterator = 0; int totalConfigs = configTypes.Count; #if UNITY_WEBGL && !UNITY_EDITOR // ============================================================ // WebGL 优化:两阶段加载 // 阶段1: 一次性并发发起所有 TextAsset 加载(同 bundle 只下载一次) // 阶段2: 从内存中分批解析,每批 Yield 防止浏览器卡死 // ============================================================ // 阶段1: 并发加载所有配置文本到内存 Debug.Log("[ConfigManager] WebGL 阶段1: 批量加载配置文件..."); var configList = configTypes.ToList(); var configDataMap = new Dictionary(totalConfigs); var loadTasks = new List(totalConfigs); foreach (var configType in configList) { var ct = configType; // closure capture loadTasks.Add(LoadConfigTextAsync(ct).ContinueWith(texts => { if (texts != null) configDataMap[ct] = texts; else Debug.LogError($"找不到配置文件: {ct.Name}"); })); } await UniTask.WhenAll(loadTasks); loadingProgress = 0.5f; // 网络加载完成 50% Debug.Log($"[ConfigManager] WebGL 阶段1完成: {configDataMap.Count}/{totalConfigs} 个配置已加载到内存"); // 阶段2: 从内存中分批解析初始化 const int parseBatchSize = 10; int parsed = 0; foreach (var configType in configList) { if (configDataMap.TryGetValue(configType, out var texts)) { InitConfigFromTexts(configType, texts); } parsed++; loadingProgress = 0.5f + 0.5f * parsed / totalConfigs; // 每 parseBatchSize 个让出主线程 if (parsed % parseBatchSize == 0) await UniTask.Yield(); } #else List loadTasks = new List(); // 逐个加载配置并更新进度 foreach (var configType in configTypes) { #if UNITY_EDITOR var sw = System.Diagnostics.Stopwatch.StartNew(); #endif UniTask uniTask = LoadConfigByTypeAsync(configType).ContinueWith(() => { #if UNITY_EDITOR if (sw.ElapsedMilliseconds >= 100) { Debug.LogWarning($"加载配置 {configType.Name} 耗时较长: {sw.ElapsedMilliseconds} ms"); } else { Debug.Log($"加载配置: {configType.Name} 用时: {sw.ElapsedMilliseconds} ms"); } sw.Stop(); #endif loadingProgress = (float)(++iterator) / totalConfigs; }); loadTasks.Add(uniTask); } await UniTask.WhenAll(loadTasks); #endif // 加载完成后设置isLoadFinished为true loadingProgress = 1f; isLoadFinished = true; } // public void LoadConfigByType(Type configType) // { // string configName = configType.Name; // if (configName.EndsWith("Config")) // { // configName = configName.Substring(0, configName.Length - 6); // } // #pragma warning disable CS0618 // Obsolete — sync legacy fallback, use LoadConfigByTypeAsync // string[] texts = ResManager.Instance.LoadConfig(configName); // #pragma warning restore CS0618 // if (texts != null) // { // string[] lines = texts; // var methodInfo = configType.GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy); // if (methodInfo != null) // { // methodInfo.Invoke(null, new object[] { lines }); // // 设置初始化标志 // var isInitField = configType.GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); // if (isInitField != null) // { // isInitField.SetValue(null, true); // } // Debug.Log($"加载配置: {configType.Name} 成功"); // } // else // { // Debug.LogError($"配置类 {configType.Name} 没有静态Init方法"); // } // } // else // { // Debug.LogError($"找不到配置文件: {configName}"); // } // } /// /// US2: Async variant of LoadConfigByType. Uses UniTask-based config loading. /// public async UniTask LoadConfigByTypeAsync(Type configType) { string configName = configType.Name; if (configName.EndsWith("Config")) { configName = configName.Substring(0, configName.Length - 6); } string[] texts = await ResManager.Instance.LoadConfigAsync(configName); if (texts != null) { var methodInfo = configType.GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy); if (methodInfo != null) { methodInfo.Invoke(null, new object[] { texts }); var isInitField = configType.GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); if (isInitField != null) { isInitField.SetValue(null, true); } } else { Debug.LogError($"配置类 {configType.Name} 没有静态Init方法"); } } else { Debug.LogError($"找不到配置文件: {configName}"); } } // private async UniTask LoadConfig() where T : class // { // string configName = typeof(T).Name; // #pragma warning disable CS0618 // string[] texts = ResManager.Instance.LoadConfig(configName); // #pragma warning restore CS0618 // if (texts != null) // { // string[] lines = texts; // var methodInfo = typeof(T).GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); // if (methodInfo != null) // { // methodInfo.Invoke(null, lines); // // 设置初始化标志 // var isInitField = typeof(T).GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); // if (isInitField != null) // { // isInitField.SetValue(null, true); // } // Debug.Log($"加载配置: {typeof(T).Name} 成功"); // } // else // { // Debug.LogError($"配置类 {typeof(T).Name} 没有静态Init方法"); // } // } // else // { // Debug.LogError($"找不到配置文件: {configName}"); // } // } public float GetLoadingProgress() { return loadingProgress; } private void ClearConfigDictionary() where T : class { // 重置 T 初始化状态 var isInitField = typeof(T).GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); if (isInitField != null) { isInitField.SetValue(null, false); } } public override void Release() { // 清空 ActLunhuidianTypeConfig 字典 ClearConfigDictionary(); // 清空 ADAwardConfig 字典 ClearConfigDictionary(); // 清空 BattleMapConfig 字典 ClearConfigDictionary(); // 清空 BeautyConfig 字典 ClearConfigDictionary(); // 清空 BeautyQualityLVConfig 字典 ClearConfigDictionary(); // 清空 BeautySkinConfig 字典 ClearConfigDictionary(); // 清空 ChestsAwardConfig 字典 ClearConfigDictionary(); // 清空 CTGConfig 字典 ClearConfigDictionary(); // 清空 DailyTaskConfig 字典 ClearConfigDictionary(); // 清空 DamageNumConfig 字典 ClearConfigDictionary(); // 清空 DirtyWordConfig 字典 ClearConfigDictionary(); // 清空 DungeonConfig 字典 ClearConfigDictionary(); // 清空 DungeonOpenTimeConfig 字典 ClearConfigDictionary(); // 清空 FaceConfig 字典 ClearConfigDictionary(); // 清空 FamilyDonateConfig 字典 ClearConfigDictionary(); // 清空 FBDJGEffectConfig 字典 ClearConfigDictionary(); // 清空 FBDJGLevelConfig 字典 ClearConfigDictionary(); // 清空 FBDJGQuickConfig 字典 ClearConfigDictionary(); // 清空 FightPowerRatioConfig 字典 ClearConfigDictionary(); // 清空 FirstChargeConfig 字典 ClearConfigDictionary(); // 清空 GoldRushCampConfig 字典 ClearConfigDictionary(); // 清空 GoldRushItemConfig 字典 ClearConfigDictionary(); // 清空 GoldRushWorkerConfig 字典 ClearConfigDictionary(); // 清空 GubaoConfig 字典 ClearConfigDictionary(); // 清空 GubaoLVAttrConfig 字典 ClearConfigDictionary(); // 清空 GubaoLVConfig 字典 ClearConfigDictionary(); // 清空 GubaoResonanceAttrConfig 字典 ClearConfigDictionary(); // 清空 GubaoResonanceConfig 字典 ClearConfigDictionary(); // 清空 GubaoStarConfig 字典 ClearConfigDictionary(); // 清空 HeroFatesConfig 字典 ClearConfigDictionary(); // 清空 HeroFatesQualityLVConfig 字典 ClearConfigDictionary(); // 清空 HeroLineupHaloConfig 字典 ClearConfigDictionary(); // 清空 HeroQualityLVConfig 字典 ClearConfigDictionary(); // 清空 HorseClassConfig 字典 ClearConfigDictionary(); // 清空 HorseSkinConfig 字典 ClearConfigDictionary(); // 清空 ItemCompoundConfig 字典 ClearConfigDictionary(); // 清空 ItemConfig 字典 ClearConfigDictionary(); // 清空 LineupRecommendConfig 字典 ClearConfigDictionary(); // 清空 LLMJConfig 字典 ClearConfigDictionary(); // 清空 MainChapterConfig 字典 ClearConfigDictionary(); // 清空 MainLevelConfig 字典 ClearConfigDictionary(); // 清空 MGGanwuLVConfig 字典 ClearConfigDictionary(); // 清空 MGGuayuQualityConfig 字典 ClearConfigDictionary(); // 清空 MGGuayuTypeConfig 字典 ClearConfigDictionary(); // 清空 MGLingyingQualityConfig 字典 ClearConfigDictionary(); // 清空 ModelConfig 字典 ClearConfigDictionary(); // 清空 NPCConfig 字典 ClearConfigDictionary(); // 清空 NPCExConfig 字典 ClearConfigDictionary(); // 清空 NPCLineupConfig 字典 ClearConfigDictionary(); // 清空 OPConfigConfig 字典 ClearConfigDictionary(); // 清空 OrderInfoConfig 字典 ClearConfigDictionary(); // 清空 PlayerAttrConfig 字典 ClearConfigDictionary(); // 清空 PlayerFaceConfig 字典 ClearConfigDictionary(); // 清空 PresetUnlockConfig 字典 ClearConfigDictionary(); // 清空 PriorBundleConfig 字典 ClearConfigDictionary(); // 清空 RandomNameConfig 字典 ClearConfigDictionary(); // 清空 SignInConfig 字典 ClearConfigDictionary(); // 清空 SkillSkinConfig 字典 ClearConfigDictionary(); // 清空 StoreConfig 字典 ClearConfigDictionary(); // 清空 SuccessConfig 字典 ClearConfigDictionary(); // 清空 SysInfoConfig 字典 ClearConfigDictionary(); // 清空 TianziConfig 字典 ClearConfigDictionary(); // 清空 TimingGiftConfig 字典 ClearConfigDictionary(); // 清空 TimingGiftTypeConfig 字典 ClearConfigDictionary(); // 清空 TitleConfig 字典 ClearConfigDictionary(); // 清空 TitleStarUpConfig 字典 ClearConfigDictionary(); // 清空 TravelEventConfig 字典 ClearConfigDictionary(); // 清空 TravelSceneryConfig 字典 ClearConfigDictionary(); // 清空 TreasureSetConfig 字典 ClearConfigDictionary(); // 清空 TreeLVConfig 字典 ClearConfigDictionary(); // 清空 WindowSearchConfig 字典 ClearConfigDictionary(); // 清空 XBGetItemConfig 字典 ClearConfigDictionary(); // 清空 ZhanlingConfig 字典 ClearConfigDictionary(); } /// /// 只加载配置文本,不做解析。用于 WebGL 批量预加载。 /// private async UniTask LoadConfigTextAsync(Type configType) { string configName = configType.Name; if (configName.EndsWith("Config")) configName = configName.Substring(0, configName.Length - 6); return await ResManager.Instance.LoadConfigAsync(configName); } /// /// 从已加载的文本初始化配置(纯内存操作,无网络)。 /// private void InitConfigFromTexts(Type configType, string[] texts) { var methodInfo = configType.GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy); if (methodInfo != null) { methodInfo.Invoke(null, new object[] { texts }); var isInitField = configType.GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); if (isInitField != null) isInitField.SetValue(null, true); } else { Debug.LogError($"配置类 {configType.Name} 没有静态Init方法"); } } }