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 TogetherSharedUpgrades v1.0.0
plugins/TogetherSharedUpgrades/TogetherSharedUpgrades.dll
Decompiled an hour agousing System; using System.Collections; 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 HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; 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("TogetherSharedUpgrades")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Host-only sharing of vanilla R.E.P.O. player upgrades, with optional catch-up for joining players.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TogetherSharedUpgrades")] [assembly: AssemblyTitle("TogetherSharedUpgrades")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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; } } } namespace TogetherSharedUpgrades { [BepInPlugin("se.repotogether.sharedupgrades", "TogetherSharedUpgrades", "1.0.0")] [BepInProcess("REPO.exe")] [BepInIncompatibility("se.repotogether15")] [BepInIncompatibility("Vippy.SharedUpgradesPlus")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "se.repotogether.sharedupgrades"; private Harmony harmony; public static ManualLogSource Log { get; private set; } public static ConfigEntry<bool> ShareUpgrades { get; private set; } public static ConfigEntry<bool> CatchUpPlayers { get; private set; } private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ShareUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("Upgrades", "Enabled", true, "Each consumed vanilla player upgrade gives the same increase to every connected teammate. Host only."); CatchUpPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Upgrades", "CatchUpPlayers", true, "Once per session/load, catch ready players up to the highest current-save level for each vanilla upgrade."); try { UpgradeSharing.ValidateGameApi(); harmony = new Harmony("se.repotogether.sharedupgrades"); harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)$"TogetherSharedUpgrades ready: enabled={ShareUpgrades.Value}; catch-up={CatchUpPlayers.Value}. Host the lobby to apply."); } catch (Exception ex) { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Log.LogError((object)("TogetherSharedUpgrades could not start; patches removed. Game API may have changed. " + ex)); ((Behaviour)this).enabled = false; } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } UpgradeSharing.ResetSession(); } } public static class UpgradeMath { public static int AddedLevels(int before, int after) { return (int)Math.Max(0L, Math.Min(2147483647L, (long)after - (long)before)); } public static int MissingLevels(int current, IEnumerable<int> teamLevels) { int num = Math.Max(0, current); foreach (int teamLevel in teamLevels) { num = Math.Max(num, teamLevel); } return AddedLevels(Math.Max(0, current), num); } public static int SafeAddition(int current, int requested) { return (int)Math.Max(0L, Math.Min(Math.Max(0, requested), 2147483647L - (long)Math.Max(0, current))); } } public static class UpgradeSharing { internal sealed class PurchaseSnapshot { internal string Buyer; internal int Generation; internal StatsManager Stats; internal Dictionary<string, int> Before; } private const string KeyPrefix = "playerUpgrade"; private static readonly string[] Suffixes = new string[13] { "CrouchRest", "DeathHeadBattery", "ExtraJump", "Health", "Launch", "MapPlayerCount", "Range", "Speed", "Stamina", "Strength", "Throw", "TumbleClimb", "TumbleWings" }; private static readonly FieldInfo DictionariesField = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries"); private static readonly FieldInfo UpgradeDoneField = AccessTools.Field(typeof(ItemUpgrade), "upgradeDone"); private static readonly FieldInfo BuyerViewField = AccessTools.Field(typeof(ItemToggle), "playerTogglePhotonID"); private static readonly FieldInfo SpawnedField = AccessTools.Field(typeof(PlayerAvatar), "spawned"); private static readonly HashSet<int> CaughtUpActors = new HashSet<int>(); private static readonly Dictionary<int, PlayerTumble> Pending = new Dictionary<int, PlayerTumble>(); private static Room sessionRoom; private static int generation; private static int sharingDepth; internal static void ValidateGameApi() { if (DictionariesField == null || !typeof(IDictionary<string, Dictionary<string, int>>).IsAssignableFrom(DictionariesField.FieldType) || UpgradeDoneField == null || UpgradeDoneField.FieldType != typeof(bool) || BuyerViewField == null || BuyerViewField.FieldType != typeof(int) || SpawnedField == null || SpawnedField.FieldType != typeof(bool)) { throw new NotSupportedException("This game version has changed the upgrade API. Update TogetherSharedUpgrades."); } } public static void ResetSession() { generation++; CaughtUpActors.Clear(); Pending.Clear(); sessionRoom = PhotonNetwork.CurrentRoom; } private static void EnsureSession() { if (sessionRoom != PhotonNetwork.CurrentRoom) { ResetSession(); } } private static bool Enabled() { if (Plugin.ShareUpgrades.Value) { return SemiFunc.IsMasterClientOrSingleplayer(); } return false; } private static IDictionary<string, Dictionary<string, int>> GetStats() { if (!((Object)(object)StatsManager.instance == (Object)null)) { return DictionariesField.GetValue(StatsManager.instance) as IDictionary<string, Dictionary<string, int>>; } return null; } private static int Level(IDictionary<string, Dictionary<string, int>> stats, string suffix, string player) { if (!stats.TryGetValue("playerUpgrade" + suffix, out var value) || !value.TryGetValue(player, out var value2)) { return 0; } return value2; } internal static PurchaseSnapshot BeforePurchase(ItemUpgrade item) { if (!Enabled() || sharingDepth != 0 || (Object)(object)item == (Object)null || !item.isPlayerUpgrade) { return null; } EnsureSession(); if ((bool)UpgradeDoneField.GetValue(item)) { return null; } ItemToggle component = ((Component)item).GetComponent<ItemToggle>(); if ((Object)(object)component == (Object)null || !component.toggleState) { return null; } PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID((int)BuyerViewField.GetValue(component)); if ((Object)(object)val == (Object)null) { return null; } string text = SemiFunc.PlayerGetSteamID(val); IDictionary<string, Dictionary<string, int>> stats = GetStats(); if (string.IsNullOrEmpty(text) || stats == null) { return null; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); string[] suffixes = Suffixes; foreach (string text2 in suffixes) { if (stats.ContainsKey("playerUpgrade" + text2)) { dictionary.Add(text2, Level(stats, text2, text)); } } return new PurchaseSnapshot { Buyer = text, Generation = generation, Stats = StatsManager.instance, Before = dictionary }; } internal static void AfterPurchase(ItemUpgrade item, PurchaseSnapshot snapshot) { if (snapshot == null || !Enabled() || sharingDepth != 0 || snapshot.Generation != generation || (Object)(object)snapshot.Stats != (Object)(object)StatsManager.instance || !(bool)UpgradeDoneField.GetValue(item)) { return; } IDictionary<string, Dictionary<string, int>> stats = GetStats(); if (stats == null || (Object)(object)PunManager.instance == (Object)null) { return; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (KeyValuePair<string, int> item2 in snapshot.Before) { int num = UpgradeMath.AddedLevels(item2.Value, Level(stats, item2.Key, snapshot.Buyer)); if (num > 0) { dictionary.Add(item2.Key, num); } } if (dictionary.Count == 0) { return; } sharingDepth++; try { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { snapshot.Buyer }; PlayerAvatar[] array = SemiFunc.PlayerGetAll().ToArray(); foreach (PlayerAvatar val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null) { continue; } string text = SemiFunc.PlayerGetSteamID(val); if (string.IsNullOrEmpty(text) || !hashSet.Add(text)) { continue; } foreach (KeyValuePair<string, int> item3 in dictionary) { SendUpgrade(val, text, item3.Key, item3.Value, stats); } } Plugin.Log.LogInfo((object)("Shared consumed upgrade: " + string.Join(", ", dictionary.Select((KeyValuePair<string, int> pair) => pair.Key + " +" + pair.Value).ToArray()))); } finally { sharingDepth--; } } private static void SendUpgrade(PlayerAvatar player, string id, string suffix, int requested, IDictionary<string, Dictionary<string, int>> stats) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) int num = UpgradeMath.SafeAddition(Level(stats, suffix, id), requested); if (num == 0 || !stats.ContainsKey("playerUpgrade" + suffix)) { return; } if (SemiFunc.IsMultiplayer()) { PhotonView component = ((Component)PunManager.instance).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("PunManager has no PhotonView."); } if (player.photonView.Owner != null && PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.GetPlayer(player.photonView.OwnerActorNr, false) != null) { component.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { id, suffix, num }); } } else { PunManager.instance.TesterUpgradeCommandRPC(id, suffix, num, default(PhotonMessageInfo)); } } internal static void PlayerReady(PlayerTumble tumble) { if (!Enabled() || !Plugin.CatchUpPlayers.Value || !SemiFunc.IsMultiplayer() || (Object)(object)tumble == (Object)null || (Object)(object)tumble.playerAvatar == (Object)null) { return; } EnsureSession(); PlayerAvatar playerAvatar = tumble.playerAvatar; if (!((Object)(object)playerAvatar.photonView == (Object)null) && playerAvatar.photonView.Owner != null) { int ownerActorNr = playerAvatar.photonView.OwnerActorNr; if (!CaughtUpActors.Contains(ownerActorNr) && (!Pending.TryGetValue(ownerActorNr, out var value) || !((Object)(object)value != (Object)null))) { Pending[ownerActorNr] = tumble; ((MonoBehaviour)tumble).StartCoroutine(CatchUpWhenSpawned(tumble, ownerActorNr, generation, sessionRoom)); } } } private static IEnumerator CatchUpWhenSpawned(PlayerTumble tumble, int actor, int queuedGeneration, Room queuedRoom) { try { float deadline = Time.realtimeSinceStartup + 90f; PlayerAvatar player = tumble.playerAvatar; while ((Object)(object)player != (Object)null && !(bool)SpawnedField.GetValue(player)) { if (!IsCurrent(queuedGeneration, queuedRoom) || Time.realtimeSinceStartup > deadline) { yield break; } yield return null; } yield return (object)new WaitForSecondsRealtime(1f); if (!((Object)(object)player == (Object)null) && IsCurrent(queuedGeneration, queuedRoom) && Enabled() && Plugin.CatchUpPlayers.Value && !((Object)(object)PunManager.instance == (Object)null)) { string text = SemiFunc.PlayerGetSteamID(player); IDictionary<string, Dictionary<string, int>> stats = GetStats(); if (!string.IsNullOrEmpty(text) && stats != null && queuedRoom.GetPlayer(actor, false) != null && CaughtUpActors.Add(actor)) { CatchUpPlayer(player, text, stats); } } } finally { if (queuedGeneration == generation && Pending.TryGetValue(actor, out var value) && value == tumble) { Pending.Remove(actor); } } } private static bool IsCurrent(int queuedGeneration, Room queuedRoom) { if (queuedGeneration == generation && queuedRoom != null) { return queuedRoom == PhotonNetwork.CurrentRoom; } return false; } private static void CatchUpPlayer(PlayerAvatar player, string id, IDictionary<string, Dictionary<string, int>> stats) { sharingDepth++; try { int num = 0; string[] suffixes = Suffixes; foreach (string text in suffixes) { if (stats.TryGetValue("playerUpgrade" + text, out var value)) { int num2 = UpgradeMath.MissingLevels(Level(stats, text, id), from pair in value where pair.Key != id select pair.Value); if (num2 > 0) { SendUpgrade(player, id, text, num2, stats); num++; } } } if (num > 0) { Plugin.Log.LogInfo((object)("Caught up joining player with " + num + " upgrade types.")); } } catch (Exception ex) { Plugin.Log.LogError((object)("Could not finish upgrade catch-up: " + ex)); } finally { sharingDepth--; } } } [HarmonyPatch(typeof(ItemUpgrade), "PlayerUpgrade")] internal static class ShareConsumedUpgradePatch { private static void Prefix(ItemUpgrade __instance, out UpgradeSharing.PurchaseSnapshot __state) { __state = null; try { __state = UpgradeSharing.BeforePurchase(__instance); } catch (Exception ex) { Plugin.Log.LogError((object)("Upgrade snapshot failed: " + ex)); } } private static void Postfix(ItemUpgrade __instance, UpgradeSharing.PurchaseSnapshot __state) { try { UpgradeSharing.AfterPurchase(__instance, __state); } catch (Exception ex) { Plugin.Log.LogError((object)("Upgrade sharing failed: " + ex)); } } } [HarmonyPatch(typeof(PlayerTumble), "SetupDone")] internal static class CatchUpReadyPlayerPatch { private static void Postfix(PlayerTumble __instance) { try { UpgradeSharing.PlayerReady(__instance); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not schedule upgrade catch-up: " + ex)); } } } [HarmonyPatch(typeof(StatsManager), "ResetAllStats")] internal static class ResetSharedUpgradesPatch { private static void Prefix() { UpgradeSharing.ResetSession(); } } [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class LoadSharedUpgradesPatch { private static void Prefix() { UpgradeSharing.ResetSession(); } } }