From 9012b333857ee8c57bc24e917fb40efa86b27288 Mon Sep 17 00:00:00 2001 From: "Quentin E. / iDeath" Date: Sat, 23 Apr 2022 04:46:39 +0200 Subject: [PATCH] Fix custom achievements (#391) * fix custom achievements for current patch * bump game ver * always use latest package for game libs * use foreach --- R2API/R2API.cs | 24 +++++++------ R2API/R2API.csproj | 10 ++++-- R2API/UnlockableAPI.cs | 63 +++++++++++---------------------- R2API/libs/RoR2BepInExPack.dll | Bin 0 -> 19968 bytes 4 files changed, 40 insertions(+), 57 deletions(-) create mode 100644 R2API/libs/RoR2BepInExPack.dll diff --git a/R2API/R2API.cs b/R2API/R2API.cs index 1c3bc682..d8ab3f96 100644 --- a/R2API/R2API.cs +++ b/R2API/R2API.cs @@ -1,10 +1,3 @@ -using BepInEx; -using BepInEx.Logging; -using MonoMod.RuntimeDetour; -using MonoMod.RuntimeDetour.HookGen; -using R2API.ContentManagement; -using R2API.Utils; -using RoR2; using System; using System.Collections.Generic; using System.IO; @@ -12,6 +5,13 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; +using BepInEx; +using BepInEx.Logging; +using MonoMod.RuntimeDetour; +using MonoMod.RuntimeDetour.HookGen; +using R2API.ContentManagement; +using R2API.Utils; +using RoR2; using UnityEngine; namespace R2API { @@ -26,9 +26,9 @@ public class R2API : BaseUnityPlugin { public const string PluginName = "R2API"; public const string PluginVersion = "0.0.1"; - private const string GameBuildId = "1.2.2.0"; + private const string GameBuildId = "1.2.3.1"; - internal new static ManualLogSource Logger { get; set; } + internal static new ManualLogSource Logger { get; set; } public static bool DebugMode { get; private set; } = false; internal static DetourModManager ModManager; @@ -88,8 +88,9 @@ public static void LogDebug(object debugText, [CallerMemberName] string caller = private static void CheckIfUsedOnRightGameVersion() { var buildId = Application.version; - if (GameBuildId == buildId) + if (GameBuildId == buildId) { return; + } Logger.LogWarning($"This version of R2API was built for build id \"{GameBuildId}\", you are running \"{buildId}\"."); Logger.LogWarning("Should any problems arise, please check for a new version before reporting issues."); @@ -173,8 +174,9 @@ private static void CheckForIncompatibleAssemblies() { , RegexOptions.Compiled | RegexOptions.IgnoreCase)) .Select(x => x.Name)); - if (info.Count == countEmpty) + if (info.Count == countEmpty) { return; + } Logger.LogBlockError(info); } diff --git a/R2API/R2API.csproj b/R2API/R2API.csproj index c129e7cc..7c7ec2ba 100644 --- a/R2API/R2API.csproj +++ b/R2API/R2API.csproj @@ -15,7 +15,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -28,9 +28,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + NU1701 @@ -40,6 +40,10 @@ libs\Facepunch.Steamworks.dll false + + libs\RoR2BepInExPack.dll + false + libs\SimpleJSON.dll false diff --git a/R2API/UnlockableAPI.cs b/R2API/UnlockableAPI.cs index 13c8f286..c8bd4d3b 100644 --- a/R2API/UnlockableAPI.cs +++ b/R2API/UnlockableAPI.cs @@ -1,17 +1,14 @@ -using Mono.Cecil.Cil; -using MonoMod.Cil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using R2API.ContentManagement; using R2API.Utils; using RoR2; using RoR2.Achievements; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using RoR2BepInExPack.VanillaFixes; using UnityEngine; -using BF = System.Reflection.BindingFlags; - namespace R2API { // Original code from Rein and Rob @@ -151,44 +148,24 @@ public static bool Loaded { [R2APISubmoduleInit(Stage = InitStage.SetHooks)] internal static void SetHooks() { - IL.RoR2.AchievementManager.CollectAchievementDefs += AddCustomAchievements; - } - - [R2APISubmoduleInit(Stage = InitStage.UnsetHooks)] - internal static void UnsetHooks() { - IL.RoR2.AchievementManager.CollectAchievementDefs -= AddCustomAchievements; + SaferAchievementManager.OnCollectAchievementDefs += AddOurDefs; } - private static void AddCustomAchievements(ILContext il) { - var achievementIdentifierField = typeof(AchievementManager).GetField(nameof(AchievementManager.achievementIdentifiers), BF.Public | BF.Static | BF.NonPublic); - if (achievementIdentifierField is null) { - throw new NullReferenceException($"Could not find field in {nameof(AchievementManager)}"); - } - - var cursor = new ILCursor(il); - cursor.GotoNext(MoveType.After, - x => x.MatchEndfinally(), - x => x.MatchLdloc(1) - ); - - void AddOurDefs(List achievementDefs, Dictionary stringToAchievementDef, List identifiers) { - for (var i = 0; i < Achievements.Count; i++) { - var achievement = Achievements[i]; - - if (achievement is null) { - continue; - } - - identifiers.Add(achievement.identifier); - achievementDefs.Add(achievement); - stringToAchievementDef.Add(achievement.identifier, achievement); + private static void AddOurDefs(List identifiers, Dictionary stringToAchievementDef, List achievementDefs) { + foreach (var achievement in Achievements) { + if (achievement is null) { + continue; } + + identifiers.Add(achievement.identifier); + achievementDefs.Add(achievement); + stringToAchievementDef.Add(achievement.identifier, achievement); } + } - cursor.Emit(OpCodes.Ldarg_0); - cursor.Emit(OpCodes.Ldsfld, achievementIdentifierField); - cursor.EmitDelegate, Dictionary, List>>(AddOurDefs); - cursor.Emit(OpCodes.Ldloc_1); + [R2APISubmoduleInit(Stage = InitStage.UnsetHooks)] + internal static void UnsetHooks() { + SaferAchievementManager.OnCollectAchievementDefs -= AddOurDefs; } internal static UnlockableDef CreateNewUnlockable(UnlockableInfo unlockableInfo) { @@ -284,7 +261,7 @@ private static UnlockableDef AddUnlockableInternal(Type unlockableType, Assembly throw new InvalidOperationException($"Too late ! Tried to add unlockable: {instance.UnlockableIdentifier} after the UnlockableCatalog"); } - string unlockableIdentifier = instance.UnlockableIdentifier; + var unlockableIdentifier = instance.UnlockableIdentifier; if (!UnlockableIdentifiers.Add(unlockableIdentifier)) { throw new InvalidOperationException($"The unlockable identifier '{unlockableIdentifier}' is already used by another mod."); @@ -294,7 +271,7 @@ private static UnlockableDef AddUnlockableInternal(Type unlockableType, Assembly unlockableDef = ScriptableObject.CreateInstance(); } - UnlockableInfo unlockableInfo = new UnlockableInfo { + var unlockableInfo = new UnlockableInfo { Name = instance.UnlockableIdentifier, HowToUnlockString = instance.GetHowToUnlock, UnlockedString = instance.GetUnlocked, diff --git a/R2API/libs/RoR2BepInExPack.dll b/R2API/libs/RoR2BepInExPack.dll new file mode 100644 index 0000000000000000000000000000000000000000..6b6763342b78c0c347e52b62d5dfa3e31138620f GIT binary patch literal 19968 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