internal static IEnumerator DelayedStart(BeatmapObjectSpawnController beatmapObjectSpawnController) { yield return(new WaitForEndOfFrame()); BeatmapObjectSpawnController = beatmapObjectSpawnController; // prone to breaking if anything else implements these interfaces BeatmapObjectManager beatmapObjectManager = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager; BeatmapObjectCallbackController coreSetup = _callbackControllerAccessor(ref beatmapObjectSpawnController) as BeatmapObjectCallbackController; IAudioTimeSource = _audioTimeSourceAccessor(ref coreSetup); IReadonlyBeatmapData beatmapData = _beatmapDataAccessor(ref coreSetup); beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber; beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber; if (Harmony.HasAnyPatches(HARMONYID)) { if (beatmapData is CustomBeatmapData customBeatmap) { if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled) { EnvironmentEnhancementManager.Init(customBeatmap, beatmapObjectSpawnController.noteLinesDistance); } } // please let me kill legacy LegacyLightHelper.Activate(beatmapData.beatmapEventsData); } }
public override void Load() { try { Logger = Log; GameOptionsData.RecommendedImpostors = GameOptionsData.MaxImpostors = Enumerable.Repeat <int>(255, 255).ToArray <int>(); Logger.LogMessage("Patching harmony..."); Logger.LogInfo("Succesfully loaded CorsacHats"); Attribute[] attrs = Attribute.GetCustomAttributes(typeof(MyHats.HatManagerHatsPatch)); foreach (Attribute attr in attrs) { Logger.LogMessage($"Looking {attr.GetType()}"); } if (attrs.Length == 0) { Logger.LogError("HarmonyPatchers not found."); } _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly()); Logger.LogMessage($"Is patched? {Harmony.HasAnyPatches(_harmony.Id)}"); } catch (Exception e) { Log.LogError(e); throw e; } }
static CrossPromotion() { if (Harmony.HasAnyPatches(_crosspromotion)) { return; } var instance = new Harmony(_crosspromotion); _ = instance.Patch( SymbolExtensions.GetMethodInfo(() => ModLister.RebuildModList()), postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => ModLister_RebuildModList_Postfix())) ); _ = instance.Patch( AccessTools.DeclaredMethod(typeof(Page_ModsConfig), nameof(Page_ModsConfig.PostClose)), postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => Page_ModsConfig_PostClose_Postfix())) ); _ = instance.Patch( AccessTools.DeclaredMethod(typeof(WorkshopItems), "Notify_Subscribed"), postfix: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => WorkshopItems_Notify_Subscribed_Postfix(new PublishedFileId_t(0)))) ); _ = instance.Patch( AccessTools.DeclaredMethod(typeof(Page_ModsConfig), nameof(Page_ModsConfig.DoWindowContents)), transpiler: new HarmonyMethod(SymbolExtensions.GetMethodInfo(() => Page_ModsConfig_DoWindowContents_Transpiler(null, null))) ); }
public static void ToggleNoodlePatches(bool value, BeatmapData beatmapData, float defaultNoteJumpMovementSpeed, float defaultNoteJumpStartBeatOffset) { if (value) { if (!Harmony.HasAnyPatches(HARMONYID)) { NoodlePatches.ForEach(n => harmony.Patch(n.originalMethod, n.prefix != null ? new HarmonyMethod(n.prefix) : null, n.postfix != null ? new HarmonyMethod(n.postfix) : null, n.transpiler != null ? new HarmonyMethod(n.transpiler) : null)); } // var njs/spawn offset stuff below // there is some ambiguity with these variables but who frikkin cares float _startHalfJumpDurationInBeats = 4; float _maxHalfJumpDistance = 18; float _moveDuration = 0.5f; foreach (BeatmapLineData beatmapLineData in beatmapData.beatmapLinesData) { foreach (BeatmapObjectData beatmapObjectData in beatmapLineData.beatmapObjectsData) { dynamic customData; if (beatmapObjectData is CustomObstacleData || beatmapObjectData is CustomNoteData) { customData = beatmapObjectData; } else { return; } dynamic dynData = customData.customData; float noteJumpMovementSpeed = (float?)Trees.at(dynData, NOTEJUMPSPEED) ?? defaultNoteJumpMovementSpeed; float noteJumpStartBeatOffset = (float?)Trees.at(dynData, SPAWNOFFSET) ?? defaultNoteJumpStartBeatOffset; // how do i not repeat this in a reasonable way float num = 60f / (float)Trees.at(dynData, "bpm"); float num2 = _startHalfJumpDurationInBeats; while (noteJumpMovementSpeed * num * num2 > _maxHalfJumpDistance) { num2 /= 2f; } num2 += noteJumpStartBeatOffset; if (num2 < 1f) { num2 = 1f; } float _jumpDuration = num * num2 * 2f; dynData.aheadTime = _moveDuration + _jumpDuration * 0.5f; } beatmapLineData.beatmapObjectsData = beatmapLineData.beatmapObjectsData.OrderBy(n => n.time - (float)((dynamic)n).customData.aheadTime).ToArray(); } } else { harmony.UnpatchAll(HARMONYID); } }
static bool Prefix(string modName) { if (Harmony.HasAnyPatches("com." + modName + ".patch")) { MPLog.Log(modName + " is not being reinstalled", LogLevel.Info); return(false); } return(true); }
private void PatchIfCheatsExist() { // Only patch on state change var cfg = Configs.zCheat; if (Configs.MagPalm.Enable.Value && (cfg.CursedPalms.Value || cfg.SizeLimit.Value > FVRPhysicalObject.FVRPhysicalObjectSize.Medium)) { Logger.LogDebug("TNH score submission disabled"); _harmonyCheat.PatchAll(typeof(ScorePatches)); } else if (Harmony.HasAnyPatches(HARMONY_GUID_CHEAT)) { Logger.LogDebug("TNH score submission enabled"); _harmonyCheat.UnpatchSelf(); } }
static void Prepare() { if (Harmony.HasAnyPatches("com.MoreLuaPower.patch")) { return; } Debug.Log("MoreLuaPower Version 2.1"); LuaPowerData.Setup(); LuaPowerCustomEnumsSetup.Setup(); if (S.I.GetComponent <PowerMonoBehavior>() == null) { S.I.gameObject.AddComponent <PowerMonoBehavior>(); } if (!LuaPowerData.customEnums[typeof(Effect)].Contains("Lua")) { LuaPowerData.customEnums[typeof(Effect)].Add("Lua"); } //MoreLuaPowerTesting.Test(); }
public WorkManagerMod(ModContentPack content) : base(content) { GetSettings <Settings>(); var harmony = new Harmony("LordKuper.WorkManager"); harmony.PatchAll(Assembly.GetExecutingAssembly()); if (Harmony.HasAnyPatches("fluffy.worktab")) { ApplyWorkTabPatch(harmony); } if (Harmony.HasAnyPatches("rimworld.moreThanCapable")) { #if DEBUG Log.Message("Work Manager: MoreThanCapable detected.", true); #endif Settings.IsBadWorkMethod = AccessTools.Method(AccessTools.TypeByName("MoreThanCapable.MoreThanCapableMod"), "IsBadWork"); } }
internal static void ToggleTechniPatches(bool value, TechniPatchType patchType) { if (value) { if (!Harmony.HasAnyPatches(HARMONYID + Enum.GetName(typeof(TechniPatchType), patchType))) { List <TechniPatchData> list = null; switch (patchType) { case TechniPatchType.LIGHTS: list = _lightPatches; break; case TechniPatchType.OBSTACLES: list = _obstaclePatches; break; case TechniPatchType.NOTES: list = _notePatches; break; case TechniPatchType.BOMBS: list = _bombPatches; break; } if (list != null) { Harmony harmony = _techniPatchInstances[patchType]; list.ForEach(n => harmony.Patch( n.OriginalMethod, n.Prefix != null ? new HarmonyMethod(n.Prefix) : null, n.Postfix != null ? new HarmonyMethod(n.Postfix) : null, n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null)); } } } else { _techniPatchInstances[patchType].UnpatchAll(HARMONYID + Enum.GetName(typeof(TechniPatchType), patchType)); } }
public void Init(HttpApplication app) { try { var patchId = GetType().FullName; var harmony = new Harmony(patchId); if (!Harmony.HasAnyPatches(patchId)) { harmony.PatchAll(); Console.WriteLine($"Module {GetType().FullName} initialized"); } } catch (Exception e) { Console.Error.WriteLine($"Module {GetType().FullName} failed to initialized"); Console.Error.WriteLine(e); } app.Error += OnError; }
public static void ToggleChromaPatches(bool value) { ChromaIsActive = value; if (value) { if (!Harmony.HasAnyPatches(HARMONYID)) { _chromaPatches.ForEach(n => _harmonyInstance.Patch( n.OriginalMethod, n.Prefix != null ? new HarmonyMethod(n.Prefix) : null, n.Postfix != null ? new HarmonyMethod(n.Postfix) : null, n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null)); } } else { _harmonyInstance.UnpatchAll(HARMONYID); } }
public void ApplyHarmonyPatches(bool enabled) { try { if (enabled && !Harmony.HasAnyPatches(HarmonyId)) { Log.Info("Applying Harmony patches..."); _harmony.PatchAll(Assembly.GetExecutingAssembly()); } else if (!enabled && Harmony.HasAnyPatches(HarmonyId)) { Log.Info("Removing Harmony patches..."); _harmony.UnpatchAll(HarmonyId); SetDataFromLevelAsync.OnUnpatch(); } } catch (Exception ex) { Log.Error("Error while applying Harmony patches.\n" + ex.ToString()); } }
public static void ToggleNoodlePatches(bool value) { if (value) { if (!Harmony.HasAnyPatches(HARMONYID)) { _noodlePatches.ForEach(n => _harmonyInstance.Patch( n.OriginalMethod, n.Prefix != null ? new HarmonyMethod(n.Prefix) : null, n.Postfix != null ? new HarmonyMethod(n.Postfix) : null, n.Transpiler != null ? new HarmonyMethod(n.Transpiler) : null)); CustomJSONData.CustomEventCallbackController.customEventCallbackControllerInit += Animation.AnimationController.CustomEventCallbackInit; } } else { _harmonyInstance.UnpatchAll(HARMONYID); CustomJSONData.CustomEventCallbackController.customEventCallbackControllerInit -= Animation.AnimationController.CustomEventCallbackInit; } }
internal static IEnumerator DelayedStart(BeatmapObjectSpawnController beatmapObjectSpawnController) { yield return(new WaitForEndOfFrame()); BeatmapObjectSpawnController = beatmapObjectSpawnController; // prone to breaking if anything else implements these interfaces BeatmapObjectManager beatmapObjectManager = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager; BeatmapObjectCallbackController coreSetup = _callbackControllerAccessor(ref beatmapObjectSpawnController) as BeatmapObjectCallbackController; IAudioTimeSource = _audioTimeSourceAccessor(ref coreSetup); IReadonlyBeatmapData beatmapData = _beatmapDataAccessor(ref coreSetup); beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber; beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber; if (Harmony.HasAnyPatches(HARMONYID)) { if (beatmapData is CustomBeatmapData customBeatmap) { if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled) { // Spaghetti code below until I can figure out a better way of doing this dynamic dynData = customBeatmap.beatmapCustomData; List <object> objectsToKill = Trees.at(dynData, ENVIRONMENTREMOVAL); // seriously what the f**k beat games // GradientBackground permanently yeeted because it looks awful and can ruin multi-colored chroma maps if (objectsToKill == null) { objectsToKill = new List <object>(); } objectsToKill.Add("GradientBackground"); if (objectsToKill != null) { IEnumerable <GameObject> gameObjects = Resources.FindObjectsOfTypeAll <GameObject>(); foreach (string s in objectsToKill.Cast <string>()) { if (s == "TrackLaneRing" || s == "BigTrackLaneRing") { foreach (GameObject n in gameObjects.Where(obj => obj.name.Contains(s))) { if (s == "TrackLaneRing" && n.name.Contains("Big")) { continue; } n.SetActive(false); } } else { foreach (GameObject n in gameObjects .Where(obj => obj.name.Contains(s) && (obj.scene.name?.Contains("Environment") ?? false) && (!obj.scene.name?.Contains("Menu") ?? false))) { n.SetActive(false); } } } } } } // please let me kill legacy LegacyLightHelper.Activate(beatmapData.beatmapEventsData); } }
public bool HasAnyPatches(string harmonyId) => Harmony.HasAnyPatches(harmonyId);
internal static IEnumerator DelayedStart() { yield return(new WaitForEndOfFrame()); AudioTimeSyncController = Resources.FindObjectsOfTypeAll <AudioTimeSyncController>().First(); BeatmapObjectSpawnController beatmapObjectSpawnController = Resources.FindObjectsOfTypeAll <BeatmapObjectSpawnController>().First(); BeatmapObjectManager beatmapObjectManager = _beatmapObjectSpawnAccessor(ref beatmapObjectSpawnController) as BeatmapObjectManager; SongBPM = beatmapObjectSpawnController.currentBpm; BeatmapObjectCallbackController coreSetup = Resources.FindObjectsOfTypeAll <BeatmapObjectCallbackController>().First(); IReadonlyBeatmapData beatmapData = coreSetup.GetField <IReadonlyBeatmapData, BeatmapObjectCallbackController>("_beatmapData"); beatmapObjectManager.noteWasCutEvent -= NoteColorizer.ColorizeSaber; beatmapObjectManager.noteWasCutEvent += NoteColorizer.ColorizeSaber; if (ChromaConfig.Instance.LightshowModifier) { foreach (BeatmapLineData b in beatmapData.beatmapLinesData) { BeatmapLineData refBeatmapLineData = b; _beatmapObjectsDataAccessor(ref refBeatmapLineData) = b.beatmapObjectsData.Where((source, index) => b.beatmapObjectsData[index].beatmapObjectType != BeatmapObjectType.Note).ToList(); } foreach (Saber saber in Resources.FindObjectsOfTypeAll <Saber>()) { saber.gameObject.SetActive(false); } BS_Utils.Gameplay.ScoreSubmission.DisableSubmission("Chroma"); if (ChromaConfig.Instance.PlayersPlace) { GameObject.Find("PlayersPlace")?.SetActive(false); } if (ChromaConfig.Instance.Spectrograms) { GameObject.Find("Spectrograms")?.SetActive(false); } if (ChromaConfig.Instance.BackColumns) { GameObject.Find("BackColumns")?.SetActive(false); } if (ChromaConfig.Instance.Buildings) { GameObject.Find("Buildings")?.SetActive(false); } } if (Harmony.HasAnyPatches(HARMONYID)) { if (beatmapData is CustomBeatmapData customBeatmap) { if (ChromaConfig.Instance.EnvironmentEnhancementsEnabled) { // Spaghetti code below until I can figure out a better way of doing this dynamic dynData = customBeatmap.beatmapCustomData; List <object> objectsToKill = Trees.at(dynData, "_environmentRemoval"); if (objectsToKill != null) { IEnumerable <GameObject> gameObjects = Resources.FindObjectsOfTypeAll <GameObject>(); foreach (string s in objectsToKill.Cast <string>()) { if (s == "TrackLaneRing" || s == "BigTrackLaneRing") { foreach (GameObject n in gameObjects.Where(obj => obj.name.Contains(s))) { if (s == "TrackLaneRing" && n.name.Contains("Big")) { continue; } n.SetActive(false); } } else { foreach (GameObject n in gameObjects .Where(obj => obj.name.Contains(s) && (obj.scene.name?.Contains("Environment") ?? false) && (!obj.scene.name?.Contains("Menu") ?? false))) { n.SetActive(false); } } } } } } // please let me kill legacy LegacyLightHelper.Activate(beatmapData.beatmapEventsData); } }
static void Main_Example() { // <create> var harmony = new Harmony("com.company.project.product"); // </create> // <debug> Harmony.DEBUG = true; // </debug> // <log> FileLog.Log("something"); // or buffered: FileLog.LogBuffered("A"); FileLog.LogBuffered("B"); FileLog.FlushBuffer(); /* don't forget to flush */ // </log> // <patch_annotation> var assembly = Assembly.GetExecutingAssembly(); harmony.PatchAll(assembly); // or implying current assembly: harmony.PatchAll(); // </patch_annotation> void PatchManual() { // <patch_manual> // add null checks to the following lines, they are omitted for clarity var original = typeof(TheClass).GetMethod("TheMethod"); var prefix = typeof(MyPatchClass1).GetMethod("SomeMethod"); var postfix = typeof(MyPatchClass2).GetMethod("SomeMethod"); harmony.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix)); // You can use named arguments to specify certain patch types only: harmony.Patch(original, postfix: new HarmonyMethod(postfix)); harmony.Patch(original, prefix: new HarmonyMethod(prefix), transpiler: new HarmonyMethod(transpiler)); // </patch_manual> // <patch_method> var harmonyPostfix = new HarmonyMethod(postfix) { priority = Priority.Low, before = new[] { "that.other.harmony.user" } }; // </patch_method> } PatchManual(); // <patch_getall> var originalMethods = Harmony.GetAllPatchedMethods(); foreach (var method in originalMethods) { } // </patch_getall> // <patch_get> var myOriginalMethods = harmony.GetPatchedMethods(); foreach (var method in myOriginalMethods) { } // </patch_get> void PatchInfo() { // <patch_info> // get the MethodBase of the original var original = typeof(TheClass).GetMethod("TheMethod"); // retrieve all patches var patches = Harmony.GetPatchInfo(original); if (patches is null) { return; // not patched } // get a summary of all different Harmony ids involved FileLog.Log("all owners: " + patches.Owners); // get info about all Prefixes/Postfixes/Transpilers foreach (var patch in patches.Prefixes) { FileLog.Log("index: " + patch.index); FileLog.Log("owner: " + patch.owner); FileLog.Log("patch method: " + patch.PatchMethod); FileLog.Log("priority: " + patch.priority); FileLog.Log("before: " + patch.before); FileLog.Log("after: " + patch.after); } // </patch_info> } PatchInfo(); // <patch_has> if (Harmony.HasAnyPatches("their.harmony.id")) { } // </patch_has> // <version> var dict = Harmony.VersionInfo(out var myVersion); FileLog.Log("My version: " + myVersion); foreach (var entry in dict) { var id = entry.Key; var version = entry.Value; FileLog.Log("Mod " + id + " uses Harmony version " + version); } // </version> }
private void SetOneColorMode(bool value) { if (value) { Logger.log.Debug("Applying One Color patches..."); var original = typeof(NoteBasicCutInfo).GetMethod("GetBasicCutInfo"); var patch = typeof(HarmonyPatches.NoteBasicCutInfo_GetBasicCutInfo).GetMethod("Postfix"); Plugin.HarmonyInstance.Patch(original, postfix: new HarmonyMethod(patch)); original = typeof(GameNoteController).GetMethod("HandleCut"); patch = typeof(HarmonyPatches.GameNoteController_HandleCut).GetMethod("Postfix"); Plugin.HarmonyInstance.Patch(original, postfix: new HarmonyMethod(patch)); original = typeof(ColorManager).GetMethod("ColorForSaberType"); patch = typeof(HarmonyPatches.ColorManager_ColorForType).GetMethod("Prefix"); Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch)); original = typeof(ColorManager).GetMethod("ColorForNoteType"); Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch)); original = typeof(BeatmapObjectSpawnController).GetMethod("SpawnNote"); patch = typeof(HarmonyPatches.BeatmapObjectSpawnController_SpawnNote).GetMethod("Prefix"); Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch)); original = typeof(NoteCutSoundEffectManager).GetMethod("BeatmapObjectCallback"); patch = typeof(HarmonyPatches.NoteCutSoundEffectManager_BeatmapObjectCallback).GetMethod("Prefix"); Plugin.HarmonyInstance.Patch(original, prefix: new HarmonyMethod(patch)); parserParams.EmitEvent("show-colors"); } else { if (Harmony.HasAnyPatches("com.steven.BeatSaber.MoreModifiers")) { Logger.log.Debug("Removing One Color patches..."); var original = typeof(NoteBasicCutInfo).GetMethod("GetBasicCutInfo"); var patch = typeof(HarmonyPatches.NoteBasicCutInfo_GetBasicCutInfo).GetMethod("Postfix"); Plugin.HarmonyInstance.Unpatch(original, patch); original = typeof(GameNoteController).GetMethod("HandleCut"); patch = typeof(HarmonyPatches.GameNoteController_HandleCut).GetMethod("Postfix"); Plugin.HarmonyInstance.Unpatch(original, patch); original = typeof(ColorManager).GetMethod("ColorForSaberType"); patch = typeof(HarmonyPatches.ColorManager_ColorForType).GetMethod("Prefix"); Plugin.HarmonyInstance.Unpatch(original, patch); original = typeof(ColorManager).GetMethod("ColorForNoteType"); Plugin.HarmonyInstance.Unpatch(original, patch); original = typeof(BeatmapObjectSpawnController).GetMethod("SpawnNote"); patch = typeof(HarmonyPatches.BeatmapObjectSpawnController_SpawnNote).GetMethod("Prefix"); Plugin.HarmonyInstance.Unpatch(original, patch); original = typeof(NoteCutSoundEffectManager).GetMethod("BeatmapObjectCallback"); patch = typeof(HarmonyPatches.NoteCutSoundEffectManager_BeatmapObjectCallback).GetMethod("Prefix"); Plugin.HarmonyInstance.Unpatch(original, patch); HarmonyPatches.GameNoteController_HandleCut.sabers = null; HarmonyPatches.GameNoteController_HandleCut.spawner = null; } } }
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()); if (!Harmony.HasAnyPatches("qkrisi.harmonymod")) { HarmonyPatchInfo.HarmonyTexture = GetComponent <TweaksStorage>().HarmonyTexture; Patching.EnsurePatch("Harmony", typeof(ModInfoPatch), typeof(WorkshopPatch), typeof(ReloadPatch), typeof(ContinueButtonPatch), typeof(ManualButtonPatch), typeof(InstructionPatch), typeof(ModManagerPatch), typeof(ChangeButtonText)); } 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; // Force the graphics quality to "Mod Quality", otherwise features might not render correctly. QualitySettings.SetQualityLevel(2); AbstractPlatformUtil.Instance.OriginalQualityLevel = 2; 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 ? "https://api.ratesapi.io/api" : "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) { ReloadPatch.ResetDict(); 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); 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; } } }; }
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { if (Harmony.HasAnyPatches("rimworld.aRandomKiwi.RimTheme")) { Log.Warning("ModCheck: RimTheme is active, will not be able to apply all patches."); foreach(var instruction in instructions) { yield return instruction; } yield break; } List<CodeInstruction> iList = new List<CodeInstruction>(instructions); #if false // debug output // This will generate the assumedMethod filling code, complete with indexes for each line { string output = ""; for (int i = 0; i < iList.Count; ++i) { string code = iList[i].ToString(); code = code.Replace("\"", "\\\""); output += "\n // "; if (i < 10) output += " "; output += i.ToString(); output += "\nassumedMethod.Add(\"" + code + "\");"; } Log.Warning(output); } #endif // first step is to verify that vanilla didn't change List<string> assumedMethod = new List<string>(); // 0 assumedMethod.Add("ldsfld System.Collections.Generic.List`1<Verse.ModContentPack> Verse.LoadedModManager::runningMods"); // 1 assumedMethod.Add("ldsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0"); // 2 assumedMethod.Add("dup NULL"); // 3 assumedMethod.Add("brtrue.s Label2"); // 4 assumedMethod.Add("pop NULL"); // 5 assumedMethod.Add("ldsfld Verse.<>c Verse.<>c::<>9"); // 6 assumedMethod.Add("ldftn System.Collections.Generic.IEnumerable`1<Verse.PatchOperation> Verse.<>c::<ApplyPatches>b__16_0(Verse.ModContentPack rm)"); // 7 assumedMethod.Add("newobj System.Void System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>>::.ctor(System.Object object, System.IntPtr method)"); // 8 assumedMethod.Add("dup NULL"); // 9 assumedMethod.Add("stsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0"); // 10 assumedMethod.Add("call static System.Collections.Generic.IEnumerable`1<Verse.PatchOperation> System.Linq.Enumerable::SelectMany(System.Collections.Generic.IEnumerable`1<Verse.ModContentPack> source, System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> selector) [Label2]"); // 11 assumedMethod.Add("callvirt abstract virtual System.Collections.Generic.IEnumerator`1<Verse.PatchOperation> System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>::GetEnumerator()"); // 12 assumedMethod.Add("stloc.0 NULL"); // 13 assumedMethod.Add("br.s Label3 [EX_BeginException]"); // 14 assumedMethod.Add("ldloc.0 NULL [Label6]"); // 15 assumedMethod.Add("callvirt abstract virtual Verse.PatchOperation System.Collections.Generic.IEnumerator`1<Verse.PatchOperation>::get_Current()"); // 16 assumedMethod.Add("stloc.1 NULL"); // 17 assumedMethod.Add("ldloc.1 NULL [EX_BeginException]"); // 18 assumedMethod.Add("ldarg.0 NULL"); // 19 assumedMethod.Add("callvirt System.Boolean Verse.PatchOperation::Apply(System.Xml.XmlDocument xml)"); // 20 assumedMethod.Add("pop NULL"); // 21 assumedMethod.Add("leave.s Label4"); // 22 assumedMethod.Add("stloc.2 NULL [EX_BeginCatch]"); // 23 assumedMethod.Add("ldstr \"Error in patch.Apply(): \""); // 24 assumedMethod.Add("ldloc.2 NULL"); // 25 assumedMethod.Add("call static System.String System.String::Concat(System.Object arg0, System.Object arg1)"); // 26 assumedMethod.Add("ldc.i4.0 NULL"); // 27 assumedMethod.Add("call static System.Void Verse.Log::Error(System.String text, System.Boolean ignoreStopLoggingLimit)"); // 28 assumedMethod.Add("leave.s Label5 [EX_EndException]"); // 29 assumedMethod.Add("ldloc.0 NULL [Label3, Label4, Label5]"); // 30 assumedMethod.Add("callvirt abstract virtual System.Boolean System.Collections.IEnumerator::MoveNext()"); // 31 assumedMethod.Add("brtrue.s Label6"); // 32 assumedMethod.Add("leave.s Label7"); // 33 assumedMethod.Add("ldloc.0 NULL [EX_BeginFinally]"); // 34 assumedMethod.Add("brfalse.s Label8"); // 35 assumedMethod.Add("ldloc.0 NULL"); // 36 assumedMethod.Add("callvirt abstract virtual System.Void System.IDisposable::Dispose()"); // 37 assumedMethod.Add("endfinally NULL [Label8, EX_EndException]"); // 38 assumedMethod.Add("ret NULL [Label7]"); //// 0 //assumedMethod.Add("ldsfld System.Collections.Generic.List`1<Verse.ModContentPack> Verse.LoadedModManager::runningMods"); //// 1 //assumedMethod.Add("ldsfld System.Func`2<Verse.ModContentPack, System.Collections.Generic.IEnumerable`1<Verse.PatchOperation>> Verse.<>c::<>9__16_0"); //// 2 //assumedMethod.Add("dup NULL"); //// 3 //assumedMethod.Add("brtrue.s Label2"); //// 4 //assumedMethod.Add("pop NULL"); //// 5 //assumedMethod.Add("newobj Void .ctor(Object, IntPtr)"); //// 6 //assumedMethod.Add("stsfld System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]] <>f__am$cache0"); //// 7 //assumedMethod.Add("ldsfld System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]] <>f__am$cache0 [Label1]"); //// 8 //assumedMethod.Add("call IEnumerable`1 SelectMany[ModContentPack,PatchOperation](IEnumerable`1, System.Func`2[Verse.ModContentPack,System.Collections.Generic.IEnumerable`1[Verse.PatchOperation]])"); //// 9 //assumedMethod.Add("callvirt IEnumerator`1 GetEnumerator()"); //// 10 //assumedMethod.Add("stloc.1 NULL"); //// 11 //assumedMethod.Add("br Label2 [EX_BeginException]"); //// 12 //assumedMethod.Add("ldloc.1 NULL [Label5]"); //// 13 //assumedMethod.Add("callvirt Verse.PatchOperation get_Current()"); //// 14 //assumedMethod.Add("stloc.0 NULL"); //// 15 //assumedMethod.Add("ldloc.0 NULL [EX_BeginException]"); //// 16 //assumedMethod.Add("ldarg.0 NULL"); //// 17 //assumedMethod.Add("callvirt Boolean Apply(System.Xml.XmlDocument)"); //// 18 //assumedMethod.Add("pop NULL"); //// 19 //assumedMethod.Add("leave Label3"); //// 20 //assumedMethod.Add("stloc.2 NULL [EX_BeginCatch]"); //// 21 //assumedMethod.Add("ldstr \"Error in patch.Apply(): \""); //// 22 //assumedMethod.Add("ldloc.2 NULL"); //// 23 //assumedMethod.Add("call System.String Concat(System.Object, System.Object)"); //// 24 //assumedMethod.Add("ldc.i4.0 NULL"); //// 25 //assumedMethod.Add("call Void Error(System.String, Boolean)"); //// 26 //assumedMethod.Add("leave Label4 [EX_EndException]"); //// 27 //assumedMethod.Add("ldloc.1 NULL [Label2, Label3, Label4]"); //// 28 //assumedMethod.Add("callvirt Boolean MoveNext()"); //// 29 //assumedMethod.Add("brtrue Label5"); //// 30 //assumedMethod.Add("leave Label6"); //// 31 //assumedMethod.Add("ldloc.1 NULL [EX_BeginFinally]"); //// 32 //assumedMethod.Add("brfalse Label7"); //// 33 //assumedMethod.Add("ldloc.1 NULL"); //// 34 //assumedMethod.Add("callvirt Void Dispose()"); //// 35 //assumedMethod.Add("endfinally NULL [Label7, EX_EndException]"); //// 36 //assumedMethod.Add("ret NULL [Label6]"); // test if the loaded method is identical to the expected method bool bFoundCorrectMethod = iList.Count == assumedMethod.Count; if(!bFoundCorrectMethod && Prefs.DevMode) { Log.Message($"current method has {iList.Count} rows, assumed {assumedMethod.Count} rows"); } // verify that each line is the same for (int i = 0; i < iList.Count && bFoundCorrectMethod; ++i) { if (iList[i].ToString() != assumedMethod[i]) { // line mismatch. Test that it isn't something silly like f__am$cache0 != f__am$cache2 bool match = false; int offset = -1; // lines with a known changing number switch (i) { case 1: case 6: case 7: offset = 119; break; case 4: offset = 37; break; } if (offset != -1) { try { // the mismatch could be a number in a generated var name // replace the char in question with the new one if (assumedMethod[i].Length > offset && assumedMethod[i].Length == iList[i].ToString().Length) { StringBuilder sb = new StringBuilder(assumedMethod[i]); sb[offset] = iList[i].ToString().ToCharArray()[offset]; // make the comparison again, this time ignoring the char, which can modify itself match = sb.ToString() == iList[i].ToString(); } } catch { // possibly unneeded try-catch. However it's here just to be safe. We really don't want to kill vanilla xml patching. } } if (!match) { // still no match. The source is indeed modified. bFoundCorrectMethod = false; if (Prefs.DevMode) Log.Message($"source-row {iList[i].ToString()} differ from assumed row {assumedMethod[i]}"); } } } if (!bFoundCorrectMethod) { Log.Error("[ModCheck] Internal failure patching Verse.LoadedModManager.ApplyPatches"); for (int i = 0; i < iList.Count; ++i) { // return the unmodified vanilla code //if (Prefs.DevMode) Log.Message(iList[i].ToString()); yield return iList[i]; } } else { // keep the method intact, but add calls to Memory to tell when patching is started or stopped. for (int i = 0; i < iList.Count; ++i) { // using indexes is perfectly safe because it works as long as vanilla is unchanged and that's already verified at this point if (i == 17) { CodeInstruction instruction = new CodeInstruction(OpCodes.Call); if (Memory.profilingEnabled) { // start the timer as well as incrementing the counter instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.startPatchingWithTimer)); } else { // no profiling, avoid the timer overhead instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.startPatching)); } yield return instruction; } else if (i == 21) { if (Memory.profilingEnabled) { CodeInstruction instruction = new CodeInstruction(OpCodes.Call); instruction.operand = typeof(ModCheck.Memory).GetMethod(nameof(ModCheck.Memory.endPatchingWithTimer)); yield return instruction; } } yield return iList[i]; } } }