示例#1
0
    static Dictionary <string, string> GetNameMap(WebsiteJSON json)
    {
        if (!(typeof(ModManager).GetField("loadedMods", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(ModManager.Instance) is Dictionary <string, Mod> loadedMods))
        {
            return(null);
        }

        var bombComponents = loadedMods.Values.SelectMany(mod => mod.GetModObjects <BombComponent>());

        var nameMap = new Dictionary <string, string>();

        foreach (var module in json.KtaneModules)
        {
            nameMap[module.Name] = module.ModuleID;
        }

        foreach (var bombComponent in bombComponents)
        {
            nameMap[bombComponent.GetModuleDisplayName()] = bombComponent.GetModuleID();
        }

        return(nameMap);
    }
    static IEnumerable <BombComponent> GetUntestedComponents(WebsiteJSON json)
    {
        int progress = 0;

        // Get local mods that need to be tested
        if (!(typeof(ModManager).GetField("loadedMods", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(ModManager.Instance) is Dictionary <string, Mod> loadedMods))
        {
            yield break;
        }

        var validLocalMods = loadedMods.Values
                             .SelectMany(mod => mod.GetModObjects <BombComponent>())
                             .Where(bombComponent =>
        {
            string moduleID = bombComponent.GetComponent <KMBombModule>()?.ModuleType ?? bombComponent.GetComponent <KMNeedyModule>()?.ModuleType;
            return(json.KtaneModules.Any(module => module.ModuleID == moduleID && module.TwitchPlays == null) || !json.KtaneModules.Any(module => module.ModuleID == moduleID));
        })
                             .ToArray();

        // Get mods that need to be loaded into the game to be tested
        var disabledParent = new GameObject();

        gameObjects.Add(disabledParent);
        disabledParent.SetActive(false);

        var modWorkshopPath = Path.GetFullPath(new[] { SteamDirectory, "steamapps", "workshop", "content", "341800" }.Aggregate(Path.Combine));
        var validModules    = json.KtaneModules.Where(module =>
        {
            if (module.TwitchPlays != null || module.SteamID == null)
            {
                return(false);
            }

            var modPath = Path.Combine(modWorkshopPath, module.SteamID);
            return(Directory.Exists(modPath));
        }).ToArray();

        // Test local mods
        int total = validLocalMods.Length + validModules.Length;

        foreach (var component in validLocalMods)
        {
            alertText.text = $"Testing compatibility of:\n\"{component.GetModuleDisplayName()}\"";
            alertProgressBar.localScale = new Vector3((float)progress / total, 1, 1);

            yield return(component);

            progress++;
        }

        // Test loaded mods
        foreach (var module in validModules)
        {
            DebugHelper.Log($"Loading module \"{module.Name}\" to test compatiablity...");
            alertText.text = $"Testing compatibility of:\n\"{module.Name}\"";
            alertProgressBar.localScale = new Vector3((float)progress / total, 1, 1);

            var      modPath       = Path.Combine(modWorkshopPath, module.SteamID);
            Mod      mod           = Mod.LoadMod(modPath, Assets.Scripts.Mods.ModInfo.ModSourceEnum.Local);
            Object[] loadedObjects = new Object[] { };
            foreach (string fileName in mod.GetAssetBundlePaths())
            {
                AssetBundle mainBundle = AssetBundle.LoadFromFile(fileName);
                if (mainBundle != null)
                {
                    try
                    {
                        mod.LoadBundle(mainBundle);
                    }
                    catch (Exception ex)
                    {
                        DebugHelper.LogException(ex, $"Load of mod \"{mod.ModID}\" failed:");
                    }

                    loadedObjects = mainBundle.LoadAllAssets <Object>();

                    mainBundle.Unload(false);
                }
            }

            mod.CallMethod("RemoveMissions");
            mod.CallMethod("RemoveSoundOverrides");

            if (mod != null)
            {
                string     ModuleID   = module.ModuleID;
                GameObject realModule = null;
                foreach (KMBombModule kmbombModule in mod.GetModObjects <KMBombModule>())
                {
                    string moduleType = kmbombModule.ModuleType;
                    if (moduleType == ModuleID)
                    {
                        realModule = Object.Instantiate(kmbombModule.gameObject, disabledParent.transform);
                        realModule.GetComponent <ModBombComponent>().OnLoadFromBundle();
                    }
                }
                foreach (KMNeedyModule kmneedyModule in mod.GetModObjects <KMNeedyModule>())
                {
                    string moduleType2 = kmneedyModule.ModuleType;
                    if (moduleType2 == ModuleID)
                    {
                        realModule = Object.Instantiate(kmneedyModule.gameObject, disabledParent.transform);
                        realModule.GetComponent <ModNeedyComponent>().OnLoadFromBundle();
                    }
                }

                if (realModule != null)
                {
                    yield return(realModule.GetComponent <BombComponent>());
                }

                mod.RemoveServiceObjects();
                mod.Unload();

                foreach (var loadedObject in loadedObjects)
                {
                    // GameObjects can't be unloaded, only destroyed.
                    if (loadedObject as GameObject)
                    {
                        Object.Destroy(loadedObject);
                        continue;
                    }

                    Resources.UnloadAsset(loadedObject);
                }
            }

            progress++;
        }

        Object.Destroy(disabledParent);
    }
    static IEnumerator TestComponents(WebsiteJSON json)
    {
        IEnumerable <BombComponent> untestedComponents = GetUntestedComponents(json);
        Dictionary <string, string> nameMap            = GetNameMap(json);

        GameObject fakeModule = new GameObject();

        gameObjects.Add(fakeModule);
        TwitchModule module = fakeModule.AddComponent <TwitchModule>();

        module.enabled = false;

        HashSet <string>          unsupportedModules = new HashSet <string>();
        Dictionary <string, bool> supportStatus      = new Dictionary <string, bool>();

        ComponentSolverFactory.SilentMode = true;

        // Try to create a ComponentSolver for each module so we can see what modules are supported.
        foreach (BombComponent bombComponent in untestedComponents)
        {
            ComponentSolver solver = null;
            try
            {
                module.BombComponent = bombComponent.GetComponent <BombComponent>();

                solver = ComponentSolverFactory.CreateSolver(module);

                module.StopAllCoroutines();                 // Stop any coroutines to prevent any exceptions or from affecting the next module.
            }
            catch (Exception e)
            {
                DebugHelper.LogException(e, $"Couldn't create a component solver for \"{bombComponent.GetModuleDisplayName()}\" during startup for the following reason:");
            }

            ModuleData.DataHasChanged |= solver != null;

            DebugHelper.Log(solver != null
                                ? $"Found a solver of type \"{solver.GetType().FullName}\" for component \"{bombComponent.GetModuleDisplayName()}\". This module is {(solver.UnsupportedModule ? "not supported" : "supported")} by Twitch Plays."
                                : $"No solver found for component \"{bombComponent.GetModuleDisplayName()}\". This module is not supported by Twitch Plays.");

            string moduleID = bombComponent.GetComponent <KMBombModule>()?.ModuleType ?? bombComponent.GetComponent <KMNeedyModule>()?.ModuleType;
            if (solver?.UnsupportedModule != false && moduleID != null)
            {
                unsupportedModules.Add(moduleID);
            }

            supportStatus[bombComponent.GetModuleDisplayName()] = !(solver?.UnsupportedModule != false && moduleID != null);

            yield return(null);
        }

        ComponentSolverFactory.SilentMode = false;
        ModuleData.WriteDataToFile();
        Object.Destroy(fakeModule);

        // Always disable the modules from the spreadsheet
        var disabledSheet = new GoogleSheet("https://spreadsheets.google.com/feeds/list/1G6hZW0RibjW7n72AkXZgDTHZ-LKj0usRkbAwxSPhcqA/3/public/values?alt=json", "modulename");

        yield return(disabledSheet);

        if (disabledSheet.Success && TwitchPlaySettings.data.AllowSheetDisabledModules)
        {
            foreach (var row in disabledSheet.GetRows())
            {
                if (!nameMap.TryGetValue(row["modulename"], out string moduleID))
                {
                    DebugHelper.Log($"Couldn't map \"{row["modulename"]}\" to a module ID when disabling modules from the spreadsheet.");
                    continue;
                }

                unsupportedModules.Add(moduleID);
            }
        }

        // Always disable modules that are marked as "Incompatible"
        foreach (var moduleInfo in json.KtaneModules)
        {
            if (moduleInfo.Compatibility != "Incompatible")
            {
                continue;
            }

            unsupportedModules.Add(moduleInfo.ModuleID);
        }

        // Using the list of unsupported module IDs stored in unsupportedModules, make a Mod Selector profile.
        string profilesPath = Path.Combine(Application.persistentDataPath, "ModProfiles");

        if (Directory.Exists(profilesPath))
        {
            Dictionary <string, object> profileData = new Dictionary <string, object>()
            {
                { "DisabledList", unsupportedModules },
                { "Operation", 1 }
            };

            File.WriteAllText(Path.Combine(profilesPath, "TP_Supported.json"), SettingsConverter.Serialize(profileData));
        }

        alertProgressBar.localScale = Vector3.one;

        // Send a message to chat if any modules aren't marked as having support
        if (supportStatus.Values.Count(status => status) > 0)
        {
            var supportedList = supportStatus.Where(pair => pair.Value).Select(pair => pair.Key).Join(", ");
            IRCConnection.SendMessage($"Let the Scoring Team know that the following modules have TP support: {supportedList}");
            alertText.text = $"These modules have TP support: {supportedList}";
            yield return(new WaitForSeconds(4));
        }
        else
        {
            alertText.text = "Support checks passed succesfully!";
            yield return(new WaitForSeconds(2));
        }

        // Log out the full results of the testing
        DebugHelper.Log($"Support testing results:\n{supportStatus.OrderByDescending(pair => pair.Value).Select(pair => $"{pair.Key} - {(pair.Value ? "" : "Not ")}Supported").Join("\n")}");
    }