Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SyncUpgrades v2.2.6
SyncUpgrades.dll
Decompiled 9 months agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using REPOLib.Modules; using SyncUpgrades.Core; using SyncUpgrades.Core.Internal; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SyncUpgrades")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.2.6.0")] [assembly: AssemblyInformationalVersion("2.2.6+560d41301aca3cc683bfddf7f157925c52b16e17")] [assembly: AssemblyProduct("SyncUpgrades")] [assembly: AssemblyTitle("SyncUpgrades")] [assembly: AssemblyVersion("2.2.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Property, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] internal sealed class CompilerFeatureRequiredAttribute : Attribute { internal CompilerFeatureRequiredAttribute(string featureName) { } } } namespace SyncUpgrades { [BepInPlugin("TGO.SyncUpgrades", "Sync Upgrades", "2.2.6")] [BepInDependency("REPOLib", "2.1.0")] public class Entry : BaseUnityPlugin { private const string PluginName = "Sync Upgrades"; private const string PluginVersion = "2.2.6"; private const string PluginId = "TGO.SyncUpgrades"; private static readonly Harmony Harmony = new Harmony("TGO.SyncUpgrades"); internal static readonly ManualLogSource LogSource = Logger.CreateLogSource("Sync Upgrades"); private static Entry? _instance; internal static ConfigFile BepConfig => ((BaseUnityPlugin)_instance).Config; private void Awake() { _instance = this; SyncManager.Init(); Harmony.PatchAllSafe(); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)4; LogSource.LogInfo((object)"Sync Upgrades loaded!"); } private void FixedUpdate() { SyncUtil.RunOneRPC(); } } } namespace SyncUpgrades.Patches { [HarmonyPatch(typeof(PunManager))] internal class PunManagerPatch { [HarmonyPrefix] [HarmonyWrapSafe] [IgnoreMethodPatchException] [HarmonyPatch("UpdateHealthRightAway", new Type[] { typeof(string) })] private static void UpdateHealthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.HealthId, "UpdateHealthRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateEnergyRightAway", new Type[] { typeof(string) })] private static void UpdateEnergyRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.StaminaId, "UpdateEnergyRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateTumbleLaunchRightAway", new Type[] { typeof(string) })] private static void UpdateTumbleLaunchRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.TumbleLaunchId, "UpdateTumbleLaunchRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateSprintSpeedRightAway", new Type[] { typeof(string) })] private static void UpdateSprintSpeedRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.SprintSpeedId, "UpdateSprintSpeedRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateGrabStrengthRightAway", new Type[] { typeof(string) })] private static void UpdateGrabStrengthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.GrabStrengthId, "UpdateGrabStrengthRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateThrowStrengthRightAway", new Type[] { typeof(string) })] private static void UpdateThrowStrengthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.ThrowStrengthId, "UpdateThrowStrengthRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateGrabRangeRightAway", new Type[] { typeof(string) })] private static void UpdateGrabRangeRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.GrabRangeId, "UpdateGrabRangeRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateExtraJumpRightAway", new Type[] { typeof(string) })] private static void UpdateExtraJumpRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.ExtraJumpId, "UpdateExtraJumpRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch("UpdateMapPlayerCountRightAway", new Type[] { typeof(string) })] private static void UpdateMapPlayerCountRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.MapPlayerCountId, "UpdateMapPlayerCountRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [IgnoreMethodPatchException] [HarmonyPatch("UpdateTumbleWingsRightAway", new Type[] { typeof(string) })] private static void UpdateTumbleWingsRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.TumbleWingsId, "UpdateTumbleWingsRightAway"); } [HarmonyPrefix] [HarmonyWrapSafe] [IgnoreMethodPatchException] [HarmonyPatch("UpdateCrouchRestRightAway", new Type[] { typeof(string) })] private static void UpdateCrouchRestRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID) { UpgradeWrapper(__instance, ___photonView, ___statsManager, _steamID, SyncUtil.CrouchRestId, "UpdateCrouchRestRightAway"); } private static void UpgradeWrapper(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID, UpgradeId upgrade, [CallerMemberName] string methodName = "Unknown Caller Method") { if (!SemiFunc.IsNotMasterClient()) { SyncManager.PlayerConsumedUpgrade(new SyncBundle(new PunManagerWrapper(__instance), ___photonView, ___statsManager, _steamID), upgrade); } } } [HarmonyPatch(typeof(RunManager))] internal class RunManagerPatch { [HarmonyPostfix] [HarmonyWrapSafe] [HarmonyPatch("ChangeLevel")] private static void ChangeLevel(bool _completedLevel, bool _levelFailed, bool ___restarting) { if (!(SemiFunc.IsNotMasterClient() || _levelFailed)) { SyncManager.SyncHostToAll(); } } } [HarmonyPatch(typeof(StatsManager))] internal class StatsManagerPatch { [HarmonyPostfix] [HarmonyWrapSafe] [HarmonyPatch("PlayerAdd", new Type[] { typeof(string), typeof(string) })] private static void PlayerAdd(string _steamID, string _playerName) { if (!SemiFunc.IsNotMasterClient() && !SemiFunc.MenuLevel()) { SyncManager.SyncHostToTarget(_steamID); } } } [HarmonyPatch(typeof(Upgrades))] internal class UpgradesPatch { [HarmonyPostfix] [HarmonyWrapSafe] [HarmonyPatch("HandleUpgradeEvent")] private static void HandleUpgradeEvent(EventData eventData) { if (SemiFunc.IsNotMasterClient()) { return; } object customData = eventData.CustomData; Hashtable val = (Hashtable)((customData is Hashtable) ? customData : null); if (val != null) { string text = (string)val[(object)"UpgradeId"]; string steamId = (string)val[(object)"SteamId"]; int newLevel = (int)val[(object)"Level"]; PlayerUpgrade val2 = default(PlayerUpgrade); if (Upgrades.TryGetUpgrade(text, ref val2)) { string rawName = SyncUtil.FixKey(text); SyncManager.PlayerConsumedUpgrade(steamId, UpgradeId.New(rawName), newLevel); } } } [HarmonyPostfix] [HarmonyWrapSafe] [HarmonyPatch("RaiseUpgradeEvent")] private static void RaiseUpgradeEvent(string upgradeId, string steamId, int level) { if (!SemiFunc.IsNotMasterClient() && !(steamId != SyncUtil.HostSteamId)) { string rawName = SyncUtil.FixKey(upgradeId); SyncManager.PlayerConsumedUpgrade(steamId, UpgradeId.New(rawName), level); } } [HarmonyPostfix] [HarmonyWrapSafe] [HarmonyPatch("RegisterUpgrade", new Type[] { typeof(string), typeof(Item), typeof(Action<PlayerAvatar, int>), typeof(Action<PlayerAvatar, int>) })] public static void RegisterUpgrade(PlayerUpgrade? __result) { if (!SemiFunc.IsNotMasterClient() && __result != null) { SyncManager.RegisterModdedUpgrade(__result); } } } } namespace SyncUpgrades.Core { internal static class Extensions { public static string SteamId(this PlayerAvatar avatar) { return SemiFunc.PlayerGetSteamID(avatar); } public static PhotonView GetView(this PunManager instance) { return ((Component)instance).GetComponent<PhotonView>(); } public static string ToName(this UpgradeType key) { return SyncUtil.GetUpgradeName(key); } public static void LogRPC(this PhotonView view, string methodName, Player target, params object[] parameters) { SyncUtil.QueueRPC(view, methodName, target, parameters); } public static void LogRPC(this PhotonView view, string methodName, RpcTarget target, params object[] parameters) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) SyncUtil.QueueRPC(view, methodName, target, parameters); } } internal interface ISyncRequest { void Run(); } public class PunManagerWrapper { [CompilerGenerated] private PunManager <manager>P; public static PunManagerWrapper instance => new PunManagerWrapper(PunManager.instance); public PunManagerWrapper(PunManager manager) { <manager>P = manager; base..ctor(); } public PhotonView GetView() { return <manager>P.GetView(); } public int UpgradePlayerTumbleWings(string steamId) { return <manager>P.UpgradePlayerTumbleWings(steamId); } public int UpgradePlayerCrouchRest(string steamId) { return <manager>P.UpgradePlayerCrouchRest(steamId); } public void SyncAllDictionaries() { <manager>P.SyncAllDictionaries(); } public int UpgradePlayerHealth(string steamId) { return <manager>P.UpgradePlayerHealth(steamId); } public int UpgradePlayerEnergy(string steamId) { return <manager>P.UpgradePlayerEnergy(steamId); } public int UpgradePlayerExtraJump(string steamId) { return <manager>P.UpgradePlayerExtraJump(steamId); } public int UpgradePlayerTumbleLaunch(string steamId) { return <manager>P.UpgradePlayerTumbleLaunch(steamId); } public int UpgradeMapPlayerCount(string steamId) { return <manager>P.UpgradeMapPlayerCount(steamId); } public int UpgradePlayerSprintSpeed(string steamId) { return <manager>P.UpgradePlayerSprintSpeed(steamId); } public int UpgradePlayerGrabStrength(string steamId) { return <manager>P.UpgradePlayerGrabStrength(steamId); } public int UpgradePlayerGrabRange(string steamId) { return <manager>P.UpgradePlayerGrabRange(steamId); } public int UpgradePlayerThrowStrength(string steamId) { return <manager>P.UpgradePlayerThrowStrength(steamId); } } internal record PunRequest(PhotonView View, string MethodName, object Target, object[] Parameters) : ISyncRequest { public static PunRequest New(PhotonView view, string methodName, object target, object[] parameters) { return new PunRequest(view, methodName, target, parameters); } public void Run() { } } [PublicAPI] public record SyncBundle(PunManagerWrapper Manager, PhotonView View, StatsManager Stats, string SteamId) { private SyncBundle(PunManagerWrapper mgr, StatsManager sts, string sId) : this(mgr, mgr.GetView(), sts, sId) { } public override string ToString() { return SteamId; } public static SyncBundle Default(string steamId) { return new SyncBundle(PunManagerWrapper.instance, StatsManager.instance, steamId); } } [PublicAPI] public static class SyncManager { private const string Section = "Default Upgrades"; private const string ModdedSection = "Modded Upgrades"; private static ConfigEntry<bool> _syncHealth = Entry.BepConfig.Bind<bool>("Default Upgrades", "Health", true, "Sync Max Health"); private static ConfigEntry<bool> _syncStamina = Entry.BepConfig.Bind<bool>("Default Upgrades", "Stamina", true, "Sync Max Stamina"); private static ConfigEntry<bool> _syncExtraJump = Entry.BepConfig.Bind<bool>("Default Upgrades", "Extra Jump", true, "Sync Extra Jump"); private static ConfigEntry<bool> _syncMapPlayerCount = Entry.BepConfig.Bind<bool>("Default Upgrades", "Map Player Count", true, "Sync Map Player Count"); private static ConfigEntry<bool> _syncGrabRange = Entry.BepConfig.Bind<bool>("Default Upgrades", "Grab Range", true, "Sync Grab Range"); private static ConfigEntry<bool> _syncGrabStrength = Entry.BepConfig.Bind<bool>("Default Upgrades", "Grab Strength", true, "Sync Grab Strength"); private static ConfigEntry<bool> _syncThrowStrength = Entry.BepConfig.Bind<bool>("Default Upgrades", "Throw Strength", true, "Sync Throw Strength"); private static ConfigEntry<bool> _syncSprintSpeed = Entry.BepConfig.Bind<bool>("Default Upgrades", "Sprint Speed", true, "Sync Sprint Speed"); private static ConfigEntry<bool> _syncTumbleLaunch = Entry.BepConfig.Bind<bool>("Default Upgrades", "Tumble Launch", true, "Sync Tumble Launch"); private static ConfigEntry<bool> _syncTumbleWings = Entry.BepConfig.Bind<bool>("Default Upgrades", "Tumble Wings", true, "Sync Tumble Wings"); private static ConfigEntry<bool> _syncCrouchRest = Entry.BepConfig.Bind<bool>("Default Upgrades", "Crouch Rest", true, "Sync Crouch Rest"); internal static ConcurrentDictionary<UpgradeId, ConfigEntry<bool>> registeredModdedUpgrades = new ConcurrentDictionary<UpgradeId, ConfigEntry<bool>>(); internal static void Init() { foreach (PlayerUpgrade playerUpgrade in Upgrades.PlayerUpgrades) { RegisterModdedUpgrade(playerUpgrade); } } public static void RegisterModdedUpgrade(PlayerUpgrade upgrade) { UpgradeId upgradeId = UpgradeId.New(SyncUtil.FixKey(upgrade.UpgradeId)); if (!registeredModdedUpgrades.ContainsKey(upgradeId)) { registeredModdedUpgrades.TryAdd(upgradeId, Entry.BepConfig.Bind<bool>("Modded Upgrades", SyncUtil.TrimKey(upgradeId.RawName), true, "Sync " + upgradeId.RawName)); } } public static void PlayerConsumedUpgrade(string steamId, UpgradeId upgrade, int newLevel) { PlayerConsumedUpgrade(SyncBundle.Default(steamId), upgrade, newLevel); } public static void PlayerConsumedUpgrade(SyncBundle bundle, UpgradeId upgrade, int newLevel = 0) { if (!ShouldSync(upgrade) || !(bundle.SteamId != SyncUtil.HostSteamId)) { return; } if (newLevel > 0) { int valueOrDefault = SyncUtil.GetUpgrades(bundle.Stats, upgrade).GetValueOrDefault(SyncUtil.HostSteamId, 0); int num = newLevel - valueOrDefault; for (int i = 0; i < num; i++) { SyncUtil.CallUpdateFunction(bundle, SyncUtil.HostSteamId, upgrade); } } else { SyncUtil.CallUpdateFunction(bundle, SyncUtil.HostSteamId, upgrade); } } public static bool SyncHostToTarget(string targetSteamId) { SyncBundle bundle = SyncBundle.Default(targetSteamId); if (!SyncHostToTarget(bundle, SyncUtil.GetPlayer(targetSteamId))) { return false; } SyncUtil.SyncStatsDictionaryToAll(bundle); return true; } public static void SyncHostToAll() { SyncHostToAll(SyncBundle.Default(SyncUtil.HostSteamId)); } public static void SyncHostToAll(SyncBundle bundle) { SyncBundle bundle2 = bundle; if ((from player in SemiFunc.PlayerGetAll().Where(NotHost).ToArray() select SyncHostToTarget(bundle2, player)).ToArray().Any()) { SyncUtil.SyncStatsDictionaryToAll(bundle2); } } private static bool SyncHostToTarget(SyncBundle bundle, PlayerAvatar target) { return SyncFromSourceToTarget(bundle, SyncUtil.Local, target); } private static bool SyncFromSourceToTarget(SyncBundle bundle, PlayerAvatar source, PlayerAvatar target) { string text = source.SteamId(); string text2 = target.SteamId(); if (text2 == text) { return false; } bool flag = false; foreach (UpgradeId item in SyncUtil.GetUpgradeTypes(bundle).Where(ShouldSync)) { Dictionary<string, int> upgrades = SyncUtil.GetUpgrades(bundle.Stats, item); int valueOrDefault = upgrades.GetValueOrDefault(text2, 0); int valueOrDefault2 = upgrades.GetValueOrDefault(text, 0); if (valueOrDefault2 <= valueOrDefault) { continue; } int num = valueOrDefault2 - valueOrDefault; if (flag = flag || num > 0) { if (item.Type != 0) { SyncUtil.AddToStatsDictionary(bundle, text2, item, num); } else { SyncUtil.UpgradeModded(bundle, target, item, num); } Entry.LogSource.LogInfo((object)string.Format("[{0}] Synchronized upgrade for player {1}: {2} ({3}), from {4} to {5}", "SyncFromSourceToTarget", text2, item.RawName, item.Type, valueOrDefault, valueOrDefault2)); } } return flag; } private static bool NotHost(PlayerAvatar avatar) { return avatar.SteamId() != SyncUtil.HostSteamId; } private static bool ShouldSync(UpgradeId key) { ConfigEntry<bool> value; return key.Type switch { UpgradeType.Health => _syncHealth.Value, UpgradeType.Stamina => _syncStamina.Value, UpgradeType.ExtraJump => _syncExtraJump.Value, UpgradeType.TumbleLaunch => _syncTumbleLaunch.Value, UpgradeType.MapPlayerCount => _syncMapPlayerCount.Value, UpgradeType.SprintSpeed => _syncSprintSpeed.Value, UpgradeType.GrabStrength => _syncGrabStrength.Value, UpgradeType.GrabRange => _syncGrabRange.Value, UpgradeType.ThrowStrength => _syncThrowStrength.Value, UpgradeType.TumbleWings => _syncTumbleWings.Value, UpgradeType.CrouchRest => _syncCrouchRest.Value, UpgradeType.Modded => registeredModdedUpgrades.TryGetValue(key, out value) && value.Value, _ => false, }; } } internal record SyncRequest(SyncBundle Bundle) : ISyncRequest { public void Run() { Bundle.Manager.SyncAllDictionaries(); } public static ISyncRequest New(SyncBundle bundle) { return new SyncRequest(bundle); } } [PublicAPI] public static class SyncUtil { private const RpcTarget Others = 1; private const string PlayerUpgrade = "playerUpgrade"; private const string AppliedPlayerUpgrade = "appliedPlayerUpgrade"; public static readonly UpgradeId HealthId = new UpgradeId(UpgradeType.Health); public static readonly UpgradeId StaminaId = new UpgradeId(UpgradeType.Stamina); public static readonly UpgradeId ExtraJumpId = new UpgradeId(UpgradeType.ExtraJump); public static readonly UpgradeId TumbleLaunchId = new UpgradeId(UpgradeType.TumbleLaunch); public static readonly UpgradeId MapPlayerCountId = new UpgradeId(UpgradeType.MapPlayerCount); public static readonly UpgradeId SprintSpeedId = new UpgradeId(UpgradeType.SprintSpeed); public static readonly UpgradeId GrabStrengthId = new UpgradeId(UpgradeType.GrabStrength); public static readonly UpgradeId GrabRangeId = new UpgradeId(UpgradeType.GrabRange); public static readonly UpgradeId ThrowStrengthId = new UpgradeId(UpgradeType.ThrowStrength); public static readonly UpgradeId TumbleWingsId = new UpgradeId(UpgradeType.TumbleWings); public static readonly UpgradeId CrouchRestId = new UpgradeId(UpgradeType.CrouchRest); private static readonly ConcurrentQueue<ISyncRequest> SyncQueue = new ConcurrentQueue<ISyncRequest>(); public static string HostSteamId => Local.SteamId(); public static PlayerAvatar Local => SemiFunc.PlayerAvatarLocal(); public static string TrimKey(string? key) { if (string.IsNullOrEmpty(key)) { return string.Empty; } string text; if (key.StartsWith("appliedPlayerUpgrade")) { text = key; return text.Substring(20, text.Length - 20); } if (!key.StartsWith("playerUpgrade")) { return key; } text = key; return text.Substring(13, text.Length - 13); } public static string FixKey(string? key) { if (string.IsNullOrEmpty(key)) { return string.Empty; } if (!key.StartsWith("playerUpgrade")) { return "playerUpgrade" + key; } return key; } public static PlayerAvatar GetPlayer(string targetSteamId) { return SemiFunc.PlayerAvatarGetFromSteamID(targetSteamId); } public static IEnumerable<UpgradeId> GetUpgradeTypes(SyncBundle bundle) { return bundle.Stats.dictionaryOfDictionaries.Where((KeyValuePair<string, Dictionary<string, int>> kvp) => kvp.Key.StartsWith("playerUpgrade") || kvp.Key.StartsWith("appliedPlayerUpgrade")).Select(UpgradeId.New).Union(SyncManager.registeredModdedUpgrades.Keys); } public static UpgradeType GetUpgradeType(string? key) { return TrimKey(key) switch { "Health" => UpgradeType.Health, "Stamina" => UpgradeType.Stamina, "ExtraJump" => UpgradeType.ExtraJump, "Launch" => UpgradeType.TumbleLaunch, "MapPlayerCount" => UpgradeType.MapPlayerCount, "Speed" => UpgradeType.SprintSpeed, "Strength" => UpgradeType.GrabStrength, "Range" => UpgradeType.GrabRange, "Throw" => UpgradeType.ThrowStrength, "TumbleWings" => UpgradeType.TumbleWings, "CrouchRest" => UpgradeType.CrouchRest, _ => UpgradeType.Modded, }; } public static string GetUpgradeName(UpgradeType key) { return key switch { UpgradeType.Health => "Health", UpgradeType.Stamina => "Stamina", UpgradeType.ExtraJump => "ExtraJump", UpgradeType.TumbleLaunch => "Launch", UpgradeType.MapPlayerCount => "MapPlayerCount", UpgradeType.SprintSpeed => "Speed", UpgradeType.GrabStrength => "Strength", UpgradeType.GrabRange => "Range", UpgradeType.ThrowStrength => "Throw", UpgradeType.TumbleWings => "TumbleWings", UpgradeType.CrouchRest => "CrouchRest", UpgradeType.Modded => "Modded: Unknown", _ => throw new ArgumentException("Invalid UpgradeType for GetUpgradeName"), }; } public static Dictionary<string, int> GetUpgrades(StatsManager stats, UpgradeId id) { return id.Type switch { UpgradeType.Health => stats.playerUpgradeHealth, UpgradeType.Stamina => stats.playerUpgradeStamina, UpgradeType.ExtraJump => stats.playerUpgradeExtraJump, UpgradeType.TumbleLaunch => stats.playerUpgradeLaunch, UpgradeType.MapPlayerCount => stats.playerUpgradeMapPlayerCount, UpgradeType.SprintSpeed => stats.playerUpgradeSpeed, UpgradeType.GrabStrength => stats.playerUpgradeStrength, UpgradeType.GrabRange => stats.playerUpgradeRange, UpgradeType.ThrowStrength => stats.playerUpgradeThrow, UpgradeType.TumbleWings => stats.playerUpgradeTumbleWings, UpgradeType.CrouchRest => stats.playerUpgradeCrouchRest, UpgradeType.Modded => stats.dictionaryOfDictionaries[id.RawName], _ => throw new ArgumentException("Invalid UpgradeType for GetUpgrades"), }; } public static void SyncStatsDictionaryToAll(SyncBundle bundle) { SyncQueue.Enqueue(SyncRequest.New(bundle)); } public static int CallUpdateFunction(SyncBundle bundle, string steamId, UpgradeId key) { return key.Type switch { UpgradeType.Health => bundle.Manager.UpgradePlayerHealth(steamId), UpgradeType.Stamina => bundle.Manager.UpgradePlayerEnergy(steamId), UpgradeType.ExtraJump => bundle.Manager.UpgradePlayerExtraJump(steamId), UpgradeType.TumbleLaunch => bundle.Manager.UpgradePlayerTumbleLaunch(steamId), UpgradeType.MapPlayerCount => bundle.Manager.UpgradeMapPlayerCount(steamId), UpgradeType.SprintSpeed => bundle.Manager.UpgradePlayerSprintSpeed(steamId), UpgradeType.GrabStrength => bundle.Manager.UpgradePlayerGrabStrength(steamId), UpgradeType.GrabRange => bundle.Manager.UpgradePlayerGrabRange(steamId), UpgradeType.ThrowStrength => bundle.Manager.UpgradePlayerThrowStrength(steamId), UpgradeType.TumbleWings => bundle.Manager.UpgradePlayerTumbleWings(steamId), UpgradeType.CrouchRest => bundle.Manager.UpgradePlayerCrouchRest(steamId), UpgradeType.Modded => UpgradeModded(bundle, SemiFunc.PlayerAvatarGetFromSteamID(steamId), key, 1), _ => throw new ArgumentException("Invalid UpgradeType for CallUpdateFunction"), }; } public static int UpgradeModded(SyncBundle bundle, PlayerAvatar workingPlayer, UpgradeId key, int amount) { string steamId = workingPlayer.SteamId(); int num = AddToStatsDictionary(bundle, steamId, key, amount); PlayerUpgrade val = default(PlayerUpgrade); if (Upgrades.TryGetUpgrade(TrimKey(key.RawName), ref val)) { val.SetLevel(workingPlayer, num); } return num; } public static int AddToStatsDictionary(SyncBundle bundle, string steamId, UpgradeId key, int incrementAmount) { return bundle.Stats.dictionaryOfDictionaries[key.RawName][steamId] += incrementAmount; } internal static void QueueRPC(PhotonView view, string methodName, object target, object[] parameters) { SyncQueue.Enqueue(PunRequest.New(view, methodName, target, parameters)); } internal static void RunOneRPC() { if (SyncQueue.TryDequeue(out ISyncRequest result)) { result.Run(); } } } [PublicAPI] public record UpgradeId(string RawName) { public UpgradeType Type { get; } = SyncUtil.GetUpgradeType(RawName); public UpgradeId(UpgradeType upgradeType) : this(SyncUtil.GetUpgradeName(upgradeType)) { Type = upgradeType; } public override string ToString() { return "{ Type = \"" + Type.ToName() + "\", RawName = \"" + RawName + "\" }"; } public static UpgradeId New(string rawName) { return new UpgradeId(rawName); } public static UpgradeId New<T>(KeyValuePair<string, T> item) { return new UpgradeId(item.Key); } } [PublicAPI] public enum UpgradeType { Modded, Health, Stamina, ExtraJump, TumbleLaunch, MapPlayerCount, SprintSpeed, GrabStrength, GrabRange, ThrowStrength, TumbleWings, CrouchRest } } namespace SyncUpgrades.Core.Internal { internal class IgnoreMethodPatchExceptionAttribute : HarmonyAttribute { } internal static class HarmonyExtensions { private enum CallType { Prefix, Postfix, Transpiler, Finalizer, ILManipulator } private static bool IsDefined<T>(this Type type) { return type.IsDefined(typeof(T)); } private static bool IsDefined<T>(this MethodInfo method) { return method.IsDefined(typeof(T)); } private static bool IsDefined<T>(this MethodInfo method, bool inherit) { return method.IsDefined(typeof(T), inherit); } public static void PatchAllSafe(this Harmony harmony) { foreach (Type item in from t in AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly()) where t.IsDefined<HarmonyPatch>() && t.IsClass select t) { MethodInfo[] array = (from m in item.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.IsDefined<HarmonyPatch>() select m).ToArray(); foreach (MethodInfo method in array) { harmony.SafePatch(item, method); } } } private static void SafePatch(this Harmony harmony, Type @class, MethodInfo method) { Type type = ((HarmonyAttribute)(((MemberInfo)@class).GetCustomAttribute<HarmonyPatch>()?)).info.declaringType ?? ((HarmonyAttribute)(((MemberInfo)method).GetCustomAttribute<HarmonyPatch>()?)).info.declaringType ?? throw new InvalidOperationException("Could not find target @class for Harmony patch."); string text = ((HarmonyAttribute)(((MemberInfo)@class).GetCustomAttribute<HarmonyPatch>()?)).info.methodName ?? ((HarmonyAttribute)(((MemberInfo)method).GetCustomAttribute<HarmonyPatch>()?)).info.methodName ?? throw new InvalidOperationException("Could not find target method name for Harmony patch."); Type[] array = ((HarmonyAttribute)(((MemberInfo)@class).GetCustomAttribute<HarmonyPatch>()?)).info.argumentTypes ?? ((HarmonyAttribute)(((MemberInfo)method).GetCustomAttribute<HarmonyPatch>()?)).info.argumentTypes ?? null; CallType callType = GetCallType(method); MethodInfo methodInfo = AccessTools.Method(type, text, array, (Type[])null); bool flag = method.IsDefined<IgnoreMethodPatchExceptionAttribute>(); if (flag && (object)methodInfo == null) { Entry.LogSource.LogWarning((object)("[IgnoreMethodPatchException] [WARN] [NOFAIL] Failed to patch method " + text + " in " + type.FullName)); return; } try { DoRawPatch(harmony, methodInfo, method, callType); } catch (Exception innerException) { harmony.Unpatch((MethodBase)methodInfo, method); if (!flag) { throw new InvalidOperationException("Failed to patch method " + text + " in " + type.FullName, innerException); } Entry.LogSource.LogWarning((object)("[IgnoreMethodPatchException] [WARN] [NOFAIL] Failed to patch method " + text + " in " + type.FullName)); } } private static void DoRawPatch(Harmony harmony, MethodInfo targetMethod, MethodInfo method, CallType callType) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown switch (callType) { case CallType.Prefix: harmony.Patch((MethodBase)targetMethod, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); break; case CallType.Postfix: harmony.Patch((MethodBase)targetMethod, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); break; case CallType.Transpiler: harmony.Patch((MethodBase)targetMethod, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null); break; case CallType.Finalizer: harmony.Patch((MethodBase)targetMethod, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null); break; case CallType.ILManipulator: harmony.Patch((MethodBase)targetMethod, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method)); break; default: throw new InvalidOperationException($"Unknown call type: {callType}"); } } private static CallType GetCallType(MethodInfo method) { if (method.IsDefined<HarmonyPrefix>()) { return CallType.Prefix; } if (method.IsDefined<HarmonyPostfix>()) { return CallType.Postfix; } if (method.IsDefined<HarmonyTranspiler>()) { return CallType.Transpiler; } if (method.IsDefined<HarmonyFinalizer>()) { return CallType.Finalizer; } if (method.IsDefined<HarmonyILManipulator>()) { return CallType.ILManipulator; } throw new InvalidOperationException("Method " + method.Name + " does not have a valid Harmony call type defined."); } } }