yyl
2026-05-08 c6a7ac3739907e8bb7c751d408e227e19f246f49
Main/Config/ConfigManager.cs
@@ -17,16 +17,12 @@
        private set;
    }
    private List<Type> preInitConfig = new List<Type>()
    {
      typeof (FuncConfigConfig),
    };
    private float loadingProgress = 0f;
    public override async UniTask Init()
    public override void Init()
    {
        await InitConfigs();
        base.Init();
        InitConfigs();
    }
    public virtual async UniTask InitConfigs()
@@ -35,25 +31,12 @@
        await LoadConfigs();
    }
    public async UniTask PreInit()
    {
        List<UniTask> uniTasks = new List<UniTask>();
        foreach (var type in preInitConfig)
        {
            uniTasks.Add(LoadConfigByTypeAsync(type));
        }
        await UniTask.WhenAll(uniTasks);
    }
    protected async UniTask LoadConfigs()
    {
        loadingProgress = 0f;
        isLoadFinished = false;
        // 自动生成:收集所有配置类型(此段将被 ConfigGenerater.Generate 自动覆盖)
        // 自动生成:收集所有配置类型
        // 自动生成:收集所有配置类型
        // 自动生成:收集所有配置类型
        // 加载配置文件
        HashSet<Type> configTypes = new HashSet<Type>() {
            typeof(ActBillboardAwardConfig),
            typeof(ActHeroAppearArtConfig),
@@ -62,47 +45,33 @@
            typeof(ActHeroReturnArtConfig),
            typeof(ActLunhuidianTypeConfig),
            typeof(ActSignAwardConfig),
            typeof(ActSignConfig),
            typeof(ActSpecialSaleConfig),
            typeof(ActTaskConfig),
            typeof(ActTaskTempConfig),
            typeof(ActTotalRechargeConfig),
            typeof(ActTotalRechargeTempConfig),
            typeof(ActTotDayRechargeConfig),
            typeof(ActTotDayRechargeTempConfig),
            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),
@@ -112,32 +81,18 @@
            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(HeroSkinAttrConfig),
            typeof(HeroSkinConfig),
            typeof(HeroTalentConfig),
            typeof(HorseClassConfig),
            typeof(HorseIDConfig),
            typeof(HorseSkinConfig),
            typeof(IconConfig),
            typeof(ItemCompoundConfig),
            typeof(ItemConfig),
            typeof(KickOutReasonConfig),
            typeof(LanguageConfig),
            typeof(LineupRecommendConfig),
            typeof(LLMJConfig),
            typeof(MailConfig),
            typeof(MainChapterConfig),
            typeof(MainLevelConfig),
            typeof(MGGanwuLVConfig),
@@ -152,24 +107,16 @@
            typeof(OrderInfoConfig),
            typeof(PlayerAttrConfig),
            typeof(PlayerFaceConfig),
            typeof(PlayerFacePicConfig),
            typeof(PlayerLVConfig),
            typeof(PlayerPropertyConfig),
            typeof(PopWinOrderConfig),
            typeof(PresetUnlockConfig),
            typeof(PriorBundleConfig),
            typeof(RandomNameConfig),
            typeof(RealmConfig),
            typeof(RealmLVUPTaskConfig),
            typeof(RichTextMsgReplaceConfig),
            typeof(RuleConfig),
            typeof(RobotConfig),
            typeof(SignInConfig),
            typeof(SkillConfig),
            typeof(SkillSkinConfig),
            typeof(StoreConfig),
            typeof(SuccessConfig),
            typeof(SysInfoConfig),
            typeof(TaskConfig),
            typeof(TianziConfig),
            typeof(TimingGiftConfig),
            typeof(TimingGiftTypeConfig),
@@ -177,8 +124,6 @@
            typeof(TitleStarUpConfig),
            typeof(TravelEventConfig),
            typeof(TravelSceneryConfig),
            typeof(TreasureCntAwardConfig),
            typeof(TreasureItemLibConfig),
            typeof(TreasureSetConfig),
            typeof(TreeLVConfig),
            typeof(WindowSearchConfig),
@@ -186,85 +131,64 @@
            typeof(ZhanlingConfig)
        };
#if UNITY_EDITOR
        HashSet<Type> configHashSet = new HashSet<Type>();
        if (System.IO.File.Exists(Application.dataPath + "/fastConfig.txt") && Launch.Instance.isOpenConfigTesting)
        {
            string[] strConfgsArr = System.IO.File.ReadAllLines(Application.dataPath + "/fastConfig.txt");
            foreach (string str in strConfgsArr)
            {
                Type tpy = Type.GetType(str);
                configHashSet.Add(tpy);
            }
        }
        //  编辑器下加入 评估加载时常
        foreach (var config in configHashSet)
        {
            if (!configTypes.Add(config))
            {
                Debug.LogWarning($"配置 {config.Name} 已经存在于 configTypes 中,跳过添加。");
            }
        }
        List<string> fastName = new List<string>();
#endif
        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<Type, string[]>(totalConfigs);
        var loadTasks = new List<UniTask>(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<UniTask> loadTasks = new List<UniTask>();
        // 逐个加载配置并更新进度
        foreach (var configType in configTypes)
        {
#if UNITY_EDITOR
            var sw = System.Diagnostics.Stopwatch.StartNew();
#endif
            UniTask uniTask = LoadConfigByTypeAsync(configType).ContinueWith(() =>
            {
            LoadConfigByType(configType);
            sw.Stop();
#if UNITY_EDITOR
                if (sw.ElapsedMilliseconds >= 100)
                {
                    Debug.LogWarning($"加载配置 {configType.Name} 耗时较长: {sw.ElapsedMilliseconds} ms");
                }
                else
                {
                    Debug.Log($"加载配置: {configType.Name} 用时: {sw.ElapsedMilliseconds} ms");
                }
                sw.Stop();
            if (sw.ElapsedMilliseconds >= 500)
            {
                Debug.LogError($"加载配置 {configType.Name} 耗时较长: {sw.ElapsedMilliseconds} ms");
            }
            else if (sw.ElapsedMilliseconds <= 5)
            {
                fastName.Add(configType.Name);
            }
            Debug.Log($"加载配置: {configType.Name} 用时: {sw.ElapsedMilliseconds} ms");
#endif
                loadingProgress = (float)(++iterator) / totalConfigs;
            });
            loadTasks.Add(uniTask);
            loadingProgress = (float)(iterator++ + 1) / totalConfigs;
        }
#if UNITY_EDITOR
        if (Launch.Instance.isOpenConfigTesting)
        {
            System.IO.File.WriteAllText(Application.dataPath + "/fastConfig.txt", string.Join("\n", fastName));
        await UniTask.WhenAll(loadTasks);
            //加载完后卸载
            foreach (var configType in configTypes)
            {
                var methodInfo = configType.GetMethod("ForceRelease", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
                if (methodInfo != null)
                {
                    methodInfo.Invoke(null, null);
                }
            }
        }
#endif
        // 加载完成后设置isLoadFinished为true
@@ -272,64 +196,28 @@
        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}");
    //     }
    // }
    /// <summary>
    /// US2: Async variant of LoadConfigByType. Uses UniTask-based config loading.
    /// </summary>
    public async UniTask LoadConfigByTypeAsync(Type configType)
    public void LoadConfigByType(Type configType)
    {
        string configName = configType.Name;
        if (configName.EndsWith("Config"))
        {
            configName = configName.Substring(0, configName.Length - 6);
        }
        string[] texts = await ResManager.Instance.LoadConfigAsync(configName);
        string[] texts = ResManager.Instance.LoadConfig(configName);
        if (texts != null)
        {
        var methodInfo = configType.GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
            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[] { texts });
                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
            {
@@ -342,38 +230,36 @@
        }
    }
    // private async UniTask LoadConfig<T>() where T : class
    // {
    //     string configName = typeof(T).Name;
    private async UniTask LoadConfig<T>() 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}");
    //     }
    // }
        string[] texts = ResManager.Instance.LoadConfig(configName);
        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()
    {
@@ -406,6 +292,14 @@
        ClearConfigDictionary<ActLunhuidianTypeConfig>();
        // 清空 ActSignAwardConfig 字典
        ClearConfigDictionary<ActSignAwardConfig>();
        // 清空 ActSignConfig 字典
        ClearConfigDictionary<ActSignConfig>();
        // 清空 ActSpecialSaleConfig 字典
        ClearConfigDictionary<ActSpecialSaleConfig>();
        // 清空 ActTaskConfig 字典
        ClearConfigDictionary<ActTaskConfig>();
        // 清空 ActTaskTempConfig 字典
        ClearConfigDictionary<ActTaskTempConfig>();
        // 清空 ActTotalRechargeConfig 字典
        ClearConfigDictionary<ActTotalRechargeConfig>();
        // 清空 ActTotalRechargeTempConfig 字典
@@ -530,6 +424,8 @@
        ClearConfigDictionary<PriorBundleConfig>();
        // 清空 RandomNameConfig 字典
        ClearConfigDictionary<RandomNameConfig>();
        // 清空 RobotConfig 字典
        ClearConfigDictionary<RobotConfig>();
        // 清空 SignInConfig 字典
        ClearConfigDictionary<SignInConfig>();
        // 清空 SkillSkinConfig 字典
@@ -566,22 +462,9 @@
        ClearConfigDictionary<ZhanlingConfig>();
    }
    /// <summary>
    /// 只加载配置文本,不做解析。用于 WebGL 批量预加载。
    /// </summary>
    private async UniTask<string[]> LoadConfigTextAsync(Type configType)
    {
        string configName = configType.Name;
        if (configName.EndsWith("Config"))
            configName = configName.Substring(0, configName.Length - 6);
        return await ResManager.Instance.LoadConfigAsync(configName);
    }
#if UNITY_EDITOR
    /// <summary>
    /// Editor 自检:反射加载所有配置类并记录耗时。
    /// </summary>
    public async void SelfCheckAllConfigs()
    [MenuItem("Tools/Config/自检")]
    public static void CheckAndGenerateFastConfig()
    {
        // 获取 Editor Assembly
        var editorAsm = System.AppDomain.CurrentDomain.GetAssemblies()
@@ -622,8 +505,8 @@
        foreach (var className in checkClasses)
        {
            // 这里也要用 Editor Assembly 获取类型
            var cfgType = editorAsm.GetType(className) ?? Type.GetType(className);
            if (cfgType == null)
            var configType = editorAsm.GetType(className) ?? Type.GetType(className);
            if (configType == null)
            {
                Debug.LogWarning($"[自检] 未找到类型: {className}");
                continue;
@@ -632,15 +515,15 @@
            var sw = System.Diagnostics.Stopwatch.StartNew();
            // 反射调用静态Init方法
            string configName = cfgType.Name;
            string configName = configType.Name;
            if (configName.EndsWith("Config"))
                configName = configName.Substring(0, configName.Length - 6);
            string[] texts = await ResManager.Instance.LoadConfigAsync(configName);
            string[] texts = ResManager.Instance.LoadConfig(configName);
            if (texts != null)
            {
                string[] lines = texts;
                var methodInfo = cfgType.GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
                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 });
@@ -651,21 +534,21 @@
            if (sw.ElapsedMilliseconds >= 500)
            {
                Debug.LogError($"[自检] 加载配置 {cfgType.Name} 耗时较长: {sw.ElapsedMilliseconds} ms");
                Debug.LogError($"[自检] 加载配置 {configType.Name} 耗时较长: {sw.ElapsedMilliseconds} ms");
            }
            else if (sw.ElapsedMilliseconds <= 5)
            {
                fastName.Add(cfgType.Name);
                fastName.Add(configType.Name);
            }
            Debug.Log($"[自检] 加载配置: {cfgType.Name} 用时: {sw.ElapsedMilliseconds} ms");
            Debug.Log($"[自检] 加载配置: {configType.Name} 用时: {sw.ElapsedMilliseconds} ms");
        }
        // 释放所有已加载的配置
        foreach (var className in checkClasses)
        {
            var cfgType = editorAsm.GetType(className) ?? Type.GetType(className);
            if (cfgType == null) continue;
            var methodInfo = cfgType.GetMethod("ForceRelease", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
            var configType = editorAsm.GetType(className) ?? Type.GetType(className);
            if (configType == null) continue;
            var methodInfo = configType.GetMethod("ForceRelease", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.FlattenHierarchy);
            if (methodInfo != null)
            {
                methodInfo.Invoke(null, null);
@@ -676,26 +559,4 @@
        Debug.Log($"[自检] fastConfig.txt 生成完毕,快速表有:{string.Join(", ", fastName)}");
    }
#endif
    /// <summary>
    /// 从已加载的文本初始化配置(纯内存操作,无网络)。
    /// </summary>
    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方法");
        }
    }
}