public void UpdateSettingWarnings() => MainThreadQueue.Enqueue(() => { if (SettingWarning == null) { return; } bool demandSettingChanged = DemandBasedSettingCache != settings.DemandBasedModLoading; bool demandModsDisabled = DemandBasedLoading.DisabledModsCount >= 50; bool warningsEnabled = CurrentState == KMGameInfo.State.Setup && modConfig.SuccessfulRead; CaseGeneratorWarning.SetActive(warningsEnabled && CaseGeneratorSettingCache != settings.CaseGenerator); DBMLWarning.SetActive(warningsEnabled && (demandSettingChanged || demandModsDisabled)); DBMLWarning.Traverse <Text>("WarningText").text = $"The { (demandSettingChanged ? "change to the setting \"DemandBasedModLoading\"" : "") + (demandSettingChanged && demandModsDisabled ? " and " : "") + (demandModsDisabled ? $"{DemandBasedLoading.DisabledModsCount} mod{(DemandBasedLoading.DisabledModsCount == 1 ? "" : "s")} that were automatically disabled" : "") } will only take effect once you enter the Mod Manager. <i>Press \"F2\" to do that automatically!</i>"; HoldablesWarning.SetActive(warningsEnabled && HoldablesSettingCache.Count != settings.Holdables.Count || HoldablesSettingCache.Except(settings.Holdables).Any()); SettingWarningEnabled = new[] { CaseGeneratorWarning, DBMLWarning, HoldablesWarning }.Any(warning => warning.activeSelf); });
public void Awake() { Instance = this; MainThreadQueue.Initialize(); GameInfo = GetComponent <KMGameInfo>(); SettingWarning = gameObject.Traverse("UI", "SettingWarning"); AdvantageousWarning = gameObject.Traverse("UI", "AdvantageousWarning"); Tips.TipMessage = gameObject.Traverse("UI", "TipMessage"); BetterCasePicker.BombCaseGenerator = GetComponentInChildren <BombCaseGenerator>(); DemandBasedLoading.LoadingScreen = gameObject.Traverse <CanvasGroup>("UI", "LoadingModules"); CaseGeneratorWarning = MakeSettingWarning("CaseGenerator"); DBMLWarning = MakeSettingWarning("DemandBasedModLoading"); HoldablesWarning = MakeSettingWarning("Holdables"); modConfig = new ModConfig <TweakSettings>("TweakSettings", OnReadError); UpdateSettings(); StartCoroutine(Modes.LoadDefaultSettings()); DemandBasedLoading.EverLoadedModules = !settings.DemandBasedModLoading; DemandBasedSettingCache = settings.DemandBasedModLoading; HoldablesSettingCache = settings.Holdables; bool changeFadeTime = settings.FadeTime >= 0; FreeplayDevice.MAX_SECONDS_TO_SOLVE = float.MaxValue; FreeplayDevice.MIN_MODULE_COUNT = 1; if (settings.EnableModsOnlyKey) { var lastFreeplaySettings = FreeplaySettings.CreateDefaultFreeplaySettings(); lastFreeplaySettings.OnlyMods = true; ProgressionManager.Instance.RecordLastFreeplaySettings(lastFreeplaySettings); } UpdateSettingWarnings(); AdvantageousWarning.SetActive(false); // Setup API/properties other mods to interact with GameObject infoObject = new GameObject("Tweaks_Info", typeof(TweaksProperties)); infoObject.transform.parent = gameObject.transform; TweaksAPI.Setup(); // Watch the TweakSettings file for Time Mode state being changed in the office. FileSystemWatcher watcher = new FileSystemWatcher(Path.Combine(Application.persistentDataPath, "Modsettings"), "TweakSettings.json") { NotifyFilter = NotifyFilters.LastWrite }; watcher.Changed += (object source, FileSystemEventArgs e) => { if (ModConfig <TweakSettings> .SerializeSettings(userSettings) == ModConfig <TweakSettings> .SerializeSettings(modConfig.Read())) { return; } UpdateSettings(); UpdateSettingWarnings(); MainThreadQueue.Enqueue(() => StartCoroutine(ModifyFreeplayDevice(false))); }; // Setup the leaderboard controller to block the leaderboard submission requests. LeaderboardController.Install(); GetTweaks(); // Create a fake case with a bunch of anchors to trick the game when using CaseGenerator. TweaksCaseGeneratorCase = new GameObject("TweaksCaseGenerator"); TweaksCaseGeneratorCase.transform.SetParent(transform); var kmBomb = TweaksCaseGeneratorCase.AddComponent <KMBomb>(); kmBomb.IsHoldable = false; kmBomb.WidgetAreas = new List <GameObject>(); kmBomb.visualTransform = transform; kmBomb.Faces = new List <KMBombFace>(); TweaksCaseGeneratorCase.AddComponent <ModBomb>(); var kmBombFace = TweaksCaseGeneratorCase.AddComponent <KMBombFace>(); kmBombFace.Anchors = new List <Transform>(); kmBomb.Faces.Add(kmBombFace); for (int i = 0; i <= 9001; i++) { kmBombFace.Anchors.Add(transform); } // Handle scene changes UnityEngine.SceneManagement.SceneManager.sceneLoaded += (Scene scene, LoadSceneMode _) => { UpdateSettings(); UpdateSettingWarnings(); Modes.settings = Modes.modConfig.Read(); Modes.modConfig.Write(Modes.settings); if ((scene.name == "mainScene" || scene.name == "gameplayScene") && changeFadeTime) { SceneManager.Instance.RapidFadeInTime = settings.FadeTime; } switch (scene.name) { case "mainScene": if (changeFadeTime) { SceneManager.Instance.SetupState.FadeInTime = SceneManager.Instance.SetupState.FadeOutTime = SceneManager.Instance.UnlockState.FadeInTime = settings.FadeTime; } break; case "gameplayLoadingScene": var gameplayLoadingManager = FindObjectOfType <GameplayLoadingManager>(); if (settings.InstantSkip) { gameplayLoadingManager.MinTotalLoadTime = 0; } if (changeFadeTime) { gameplayLoadingManager.FadeInTime = gameplayLoadingManager.FadeOutTime = settings.FadeTime; } ReflectedTypes.UpdateTypes(); ReflectedTypes.CurrencyAPIEndpointField?.SetValue(null, settings.FixFER ? "http://api.exchangeratesapi.io" : "http://api.fixer.io"); if ( AdvantageousFeaturesEnabled && GameplayState.MissionToLoad != Assets.Scripts.Missions.FreeplayMissionGenerator.FREEPLAY_MISSION_ID && GameplayState.MissionToLoad != ModMission.CUSTOM_MISSION_ID ) { StartCoroutine(ShowAdvantageousWarning()); } break; case "gameplayScene": if (changeFadeTime) { SceneManager.Instance.GameplayState.FadeInTime = SceneManager.Instance.GameplayState.FadeOutTime = settings.FadeTime; } break; } }; // Handle state changes GameInfo.OnStateChange += (KMGameInfo.State state) => { OnStateChanged(CurrentState, state); // Transitioning away from another state if (state == KMGameInfo.State.Transitioning) { if (CurrentState == KMGameInfo.State.Setup) { DemandBasedLoading.DisabledModsCount = 0; } if (CurrentState != KMGameInfo.State.Gameplay) { DemandBasedLoading.HandleTransitioning(); } } CurrentState = state; watcher.EnableRaisingEvents = state == KMGameInfo.State.Setup; if (state == KMGameInfo.State.Gameplay) { if (AdvantageousFeaturesEnabled) { LeaderboardController.DisableLeaderboards(); } TwitchPlaysActiveCache = TwitchPlaysActive; CurrentModeCache = CurrentMode; BombStatus.Instance.widgetsActivated = false; BombStatus.Instance.HUD.SetActive(settings.BombHUD); BombStatus.Instance.ConfidencePrefab.gameObject.SetActive(CurrentMode != Mode.Zen); BombStatus.Instance.StrikesPrefab.color = CurrentMode == Mode.Time ? Color.yellow : Color.red; Modes.Multiplier = Modes.settings.TimeModeStartingMultiplier; BombStatus.Instance.UpdateMultiplier(); bombWrappers.Clear(); StartCoroutine(CheckForBombs()); if (GameplayState.BombSeedToUse == -1) { GameplayState.BombSeedToUse = settings.MissionSeed; } } else if (state == KMGameInfo.State.Setup) { if (ReflectedTypes.LoadedModsField.GetValue(ModManager.Instance) is Dictionary <string, Mod> loadedMods) { Mod tweaksMod = loadedMods.Values.FirstOrDefault(mod => mod.ModID == "Tweaks"); if (tweaksMod != null && CaseGeneratorSettingCache != settings.CaseGenerator) { if (settings.CaseGenerator) { tweaksMod.ModObjects.Add(TweaksCaseGeneratorCase); } else { tweaksMod.ModObjects.Remove(TweaksCaseGeneratorCase); } CaseGeneratorSettingCache = settings.CaseGenerator; } } StartCoroutine(Tips.ShowTip()); StartCoroutine(ModifyFreeplayDevice(true)); StartCoroutine(ModifyHoldables()); GetComponentInChildren <ModSelectorExtension>().FindAPI(); TweaksAPI.SetTPProperties(!TwitchPlaysActive); Patching.EnsurePatch("LogfileViewerHotkey", typeof(LogfileUploaderPatch)); GameplayState.BombSeedToUse = -1; UpdateSettingWarnings(); UpdateBombCreator(); } else if (state == KMGameInfo.State.Transitioning) { // Because the settings are checked on a scene change and there is no scene change from exiting the gameplay room, // we need to update the settings here in case the user changed their HideTOC settings. UpdateSettings(); bool modified = false; var ModMissionToCs = ModManager.Instance.ModMissionToCs; foreach (var metaData in ModMissionToCs) { modified |= ModToCMetaData.Add(metaData); } var unloadedMods = (Dictionary <string, Mod>)ReflectedTypes.UnloadedModsField.GetValue(ModManager.Instance); if (unloadedMods != null) { foreach (var unloadedMod in unloadedMods.Values) { var tocs = (List <ModTableOfContentsMetaData>)ReflectedTypes.TocsField.GetValue(unloadedMod); if (tocs != null) { foreach (var metaData in tocs) { modified |= ModToCMetaData.Remove(metaData); } } } } var newToCs = ModToCMetaData.Where(metaData => !settings.HideTOC.Any(pattern => Localization.GetLocalizedString(metaData.DisplayNameTerm).Like(pattern))); modified |= newToCs.Count() != ModMissionToCs.Count || !newToCs.All(ModMissionToCs.Contains); ModMissionToCs.Clear(); ModMissionToCs.AddRange(newToCs); if (modified) { SetupState.LastBombBinderTOCIndex = 0; SetupState.LastBombBinderTOCPage = 0; } } }; }
void UpdateSettingWarning() => MainThreadQueue.Enqueue(() => SettingWarning.SetActive(CurrentState == KMGameInfo.State.Setup && CaseGeneratorSettingCache != settings.CaseGenerator));
public void Awake() { MainThreadQueue.Initialize(); GameInfo = GetComponent <KMGameInfo>(); SettingWarning = transform.Find("UI").Find("SettingWarning").gameObject; BetterCasePicker.BombCaseGenerator = GetComponentInChildren <BombCaseGenerator>(); modConfig = new ModConfig <TweakSettings>("TweakSettings"); UpdateSettings(); bool changeFadeTime = settings.FadeTime >= 0; FreeplayDevice.MAX_SECONDS_TO_SOLVE = float.MaxValue; FreeplayDevice.MIN_MODULE_COUNT = 1; if (settings.EnableModsOnlyKey) { var lastFreeplaySettings = FreeplaySettings.CreateDefaultFreeplaySettings(); lastFreeplaySettings.OnlyMods = true; ProgressionManager.Instance.RecordLastFreeplaySettings(lastFreeplaySettings); } UpdateSettingWarning(); // Setup API/properties other mods to interact with GameObject infoObject = new GameObject("Tweaks_Info", typeof(TweaksProperties)); infoObject.transform.parent = gameObject.transform; // Watch the TweakSettings file for Time Mode state being changed in the office. FileSystemWatcher watcher = new FileSystemWatcher(Path.Combine(Application.persistentDataPath, "Modsettings"), "TweakSettings.json") { NotifyFilter = NotifyFilters.LastWrite }; watcher.Changed += (object source, FileSystemEventArgs e) => { if (modConfig.SerializeSettings(settings) == modConfig.SerializeSettings(modConfig.Settings)) { return; } UpdateSettings(); UpdateSettingWarning(); MainThreadQueue.Enqueue(() => StartCoroutine(ModifyFreeplayDevice(false))); }; // Setup our "service" to block the leaderboard submission requests ReflectedTypes.InstanceField.SetValue(null, new SteamFilterService()); // Create a fake case with a bunch of anchors to trick the game when using CaseGenerator. TweaksCaseGeneratorCase = new GameObject("TweaksCaseGenerator"); TweaksCaseGeneratorCase.transform.SetParent(transform); var kmBomb = TweaksCaseGeneratorCase.AddComponent <KMBomb>(); kmBomb.IsHoldable = false; kmBomb.WidgetAreas = new List <GameObject>(); kmBomb.visualTransform = transform; kmBomb.Faces = new List <KMBombFace>(); TweaksCaseGeneratorCase.AddComponent <ModBomb>(); var kmBombFace = TweaksCaseGeneratorCase.AddComponent <KMBombFace>(); kmBombFace.Anchors = new List <Transform>(); kmBomb.Faces.Add(kmBombFace); for (int i = 0; i <= 9001; i++) { kmBombFace.Anchors.Add(transform); } // Handle scene changes UnityEngine.SceneManagement.SceneManager.sceneLoaded += (Scene scene, LoadSceneMode _) => { UpdateSettings(); UpdateSettingWarning(); Modes.settings = Modes.modConfig.Settings; Modes.modConfig.Settings = Modes.settings; if ((scene.name == "mainScene" || scene.name == "gameplayScene") && changeFadeTime) { SceneManager.Instance.RapidFadeInTime = settings.FadeTime; } switch (scene.name) { case "mainScene": if (changeFadeTime) { SceneManager.Instance.SetupState.FadeInTime = SceneManager.Instance.SetupState.FadeOutTime = SceneManager.Instance.UnlockState.FadeInTime = settings.FadeTime; } break; case "gameplayLoadingScene": var gameplayLoadingManager = FindObjectOfType <GameplayLoadingManager>(); if (settings.InstantSkip) { gameplayLoadingManager.MinTotalLoadTime = 0; } if (changeFadeTime) { gameplayLoadingManager.FadeInTime = gameplayLoadingManager.FadeOutTime = settings.FadeTime; } ReflectedTypes.UpdateTypes(); ReflectedTypes.CurrencyAPIEndpointField?.SetValue(null, settings.FixFER ? "http://api.exchangeratesapi.io" : "http://api.fixer.io"); break; case "gameplayScene": if (changeFadeTime) { SceneManager.Instance.GameplayState.FadeInTime = SceneManager.Instance.GameplayState.FadeOutTime = settings.FadeTime; } break; } }; // Handle state changes GameInfo.OnStateChange += (KMGameInfo.State state) => { CurrentState = state; watcher.EnableRaisingEvents = state == KMGameInfo.State.Setup; if (state == KMGameInfo.State.Gameplay) { bool disableRecords = settings.BombHUD || settings.ShowEdgework || CurrentMode != Mode.Normal || settings.MissionSeed != -1; Assets.Scripts.Stats.StatsManager.Instance.DisableStatChanges = Assets.Scripts.Records.RecordManager.Instance.DisableBestRecords = disableRecords; if (disableRecords) { SteamFilterService.TargetMissionID = GameplayState.MissionToLoad; } BetterCasePicker.HandleCaseGeneration(); BombStatus.Instance.widgetsActivated = false; BombStatus.Instance.HUD.SetActive(settings.BombHUD); BombStatus.Instance.Edgework.SetActive(settings.ShowEdgework); BombStatus.Instance.ConfidencePrefab.gameObject.SetActive(CurrentMode != Mode.Zen); BombStatus.Instance.StrikesPrefab.color = CurrentMode == Mode.Time ? Color.yellow : Color.red; Modes.Multiplier = Modes.settings.TimeModeStartingMultiplier; BombStatus.Instance.UpdateMultiplier(); bombWrappers = new BombWrapper[] { }; StartCoroutine(CheckForBombs()); if (settings.SkipGameplayDelay) { StartCoroutine(SkipGameplayDelay()); } if (GameplayState.BombSeedToUse == -1) { GameplayState.BombSeedToUse = settings.MissionSeed; } } else if (state == KMGameInfo.State.Setup) { if (ReflectedTypes.LoadedModsField.GetValue(ModManager.Instance) is Dictionary <string, Mod> loadedMods) { Mod tweaksMod = loadedMods.Values.FirstOrDefault(mod => mod.ModID == "Tweaks"); if (tweaksMod != null) { if (CaseGeneratorSettingCache != settings.CaseGenerator) { if (settings.CaseGenerator) { tweaksMod.ModObjects.Add(TweaksCaseGeneratorCase); } else { tweaksMod.ModObjects.Remove(TweaksCaseGeneratorCase); } CaseGeneratorSettingCache = settings.CaseGenerator; UpdateSettingWarning(); } } } StartCoroutine(ModifyFreeplayDevice(true)); GetComponentInChildren <ModSelectorExtension>().FindAPI(); GameplayState.BombSeedToUse = -1; } else if (state == KMGameInfo.State.Transitioning) { // Because the settings are checked on a scene change and there is no scene change from exiting the gameplay room, // we need to update the settings here in case the user changed their HideTOC settings. UpdateSettings(); bool modified = false; var ModMissionToCs = ModManager.Instance.ModMissionToCs; foreach (var metaData in ModMissionToCs) { modified |= ModToCMetaData.Add(metaData); } var unloadedMods = (Dictionary <string, Mod>)ReflectedTypes.UnloadedModsField.GetValue(ModManager.Instance); if (unloadedMods != null) { foreach (var unloadedMod in unloadedMods.Values) { var tocs = (List <ModTableOfContentsMetaData>)ReflectedTypes.TocsField.GetValue(unloadedMod); if (tocs != null) { foreach (var metaData in tocs) { modified |= ModToCMetaData.Remove(metaData); } } } } var newToCs = ModToCMetaData.Where(metaData => !settings.HideTOC.Any(pattern => Localization.GetLocalizedString(metaData.DisplayNameTerm).Like(pattern))); modified |= (newToCs.Count() != ModMissionToCs.Count || !newToCs.All(ModMissionToCs.Contains)); ModMissionToCs.Clear(); ModMissionToCs.AddRange(newToCs); if (modified) { SetupState.LastBombBinderTOCIndex = 0; SetupState.LastBombBinderTOCPage = 0; } } }; }
private void OutputThreadMethod(TextWriter output) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); _messageDelay = 0; while (ThreadAlive) { try { Thread.Sleep(25); IRCCommand command; if (stopWatch.ElapsedMilliseconds <= _messageDelay && _state == IRCConnectionState.Connected) { continue; } lock (_sendQueue) { if (_sendQueue.Count == 0) { continue; } command = _sendQueue.Dequeue(); } if (command.CommandIsColor() && CurrentColor.Equals(command.GetColor(), StringComparison.InvariantCultureIgnoreCase)) { continue; } output.WriteLine(command.Command); output.Flush(); stopWatch.Reset(); stopWatch.Start(); _messageDelay = _isModerator ? MessageDelayMod : MessageDelayUser; _messageDelay += (command.CommandIsColor() && _isModerator) ? 700 : 0; } catch { AddTextToHoldable("[IRC:Disconnect] Connection failed."); _state = IRCConnectionState.Disconnected; } } MainThreadQueue.Enqueue(() => TwitchGame.EnableDisableInput()); try { if (_state == IRCConnectionState.Disconnecting) { IRCCommand setColor = new IRCCommand($"PRIVMSG #{_settings.channelName} :.color {ColorOnDisconnect}"); if (setColor.CommandIsColor()) { AddTextToHoldable("[IRC:Disconnect] Color {0} was requested, setting it now.", ColorOnDisconnect); while (stopWatch.ElapsedMilliseconds < _messageDelay) { Thread.Sleep(25); } output.WriteLine(setColor.Command); output.Flush(); stopWatch.Reset(); stopWatch.Start(); while (stopWatch.ElapsedMilliseconds < 1200) { Thread.Sleep(25); } } _state = IRCConnectionState.Disconnected; AddTextToHoldable("[IRC:Disconnect] Disconnected from chat IRC."); } lock (_sendQueue) _sendQueue.Clear(); } catch { _state = IRCConnectionState.Disconnected; } MainThreadQueue.Enqueue(() => { if (!gameObject.activeInHierarchy) { AddTextToHoldable("[IRC:Disconnect] Twitch Plays disabled."); } }); }
private void InputThreadMethod(TextReader input, NetworkStream networkStream) { bool pingTimeoutTest = false; // Keeps track of if we are currently in a ping timeout test. Stopwatch stopwatch = new Stopwatch(); try { stopwatch.Start(); while (ThreadAlive) { if (_state == IRCConnectionState.Connected) { // If the server hasn't sent any data for 6 minutes then begin a ping timeout test. if (stopwatch.ElapsedMilliseconds > 360000 && !pingTimeoutTest) { pingTimeoutTest = true; stopwatch.Reset(); stopwatch.Start(); SendCommand("PING"); MainThreadQueue.Enqueue(() => ConnectionAlert.SetActive(true)); } else if (pingTimeoutTest) { alertText.text = $"The bot might be disconnected from the server. Timing out in {(10 - stopwatch.ElapsedMilliseconds / 1000f).ToString("N1")}"; alertProgressBar.localScale = new Vector3(1 - stopwatch.ElapsedMilliseconds / 10000f, 1, 1); if (stopwatch.ElapsedMilliseconds > 10000) // Timeout if there hasn't been a response to the ping for 10s. { AddTextToHoldable("[IRC:Connect] Connection timed out."); stopwatch.Reset(); _state = IRCConnectionState.Disconnected; continue; } } } if (!networkStream.DataAvailable) { Thread.Sleep(25); continue; } pingTimeoutTest = false; MainThreadQueue.Enqueue(() => ConnectionAlert.SetActive(false)); stopwatch.Reset(); stopwatch.Start(); string buffer = input.ReadLine(); MainThreadQueue.Enqueue(() => { foreach (ActionMap action in Actions) { if (action.TryMatch(buffer)) { break; } } }); } } catch { AddTextToHoldable("[IRC:Disconnect] Connection failed."); _state = IRCConnectionState.Disconnected; } }