using 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.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Steamworks; 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("CrewUpgrades")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("CrewUpgrades")] [assembly: AssemblyTitle("CrewUpgrades")] [assembly: AssemblyVersion("1.0.1.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; } } } namespace CrewUpgrades { internal static class MyPluginInfo { public const string PLUGIN_GUID = "com.deegamenchill.crewupgrades"; public const string PLUGIN_NAME = "CrewUpgrades"; public const string PLUGIN_VERSION = "1.0.1"; } [BepInPlugin("com.deegamenchill.crewupgrades", "CrewUpgrades", "1.0.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; new Harmony("com.deegamenchill.crewupgrades").PatchAll(); Log.LogInfo((object)"com.deegamenchill.crewupgrades v1.0.1 loaded."); } } } namespace CrewUpgrades.Patches { [HarmonyPatch(typeof(ItemUpgrade), "PlayerUpgrade")] [HarmonyWrapSafe] public static class CrewUpgradesPatch { public struct UpgradeContext { public string SteamID; public int ViewID; public string PlayerName; public Dictionary PreUpgradeStats; } public static HashSet SyncedPlayers = new HashSet(); [HarmonyPrefix] public static void Prefix(ItemUpgrade __instance, out UpgradeContext __state) { __state = default(UpgradeContext); if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } object? value = AccessTools.Field(typeof(ItemUpgrade), "itemToggle").GetValue(__instance); ItemToggle val = (ItemToggle)((value is ItemToggle) ? value : null); if ((Object)(object)val == (Object)null || !val.toggleState) { return; } int num = (int)AccessTools.Field(typeof(ItemToggle), "playerTogglePhotonID").GetValue(val); PlayerAvatar val2 = SemiFunc.PlayerAvatarGetFromPhotonID(num); if ((Object)(object)val2 == (Object)null) { return; } string playerName = (string)AccessTools.Field(typeof(PlayerAvatar), "playerName").GetValue(val2); string text = (string)AccessTools.Field(typeof(PlayerAvatar), "steamID").GetValue(val2); Dictionary dictionary = new Dictionary(); SortedDictionary> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance); if (dictOfDicts != null) { foreach (KeyValuePair> item in dictOfDicts) { if (item.Key.StartsWith("playerUpgrade")) { dictionary[item.Key] = (item.Value.TryGetValue(text, out var value2) ? value2 : 0); } } } __state = new UpgradeContext { SteamID = text, ViewID = num, PlayerName = playerName, PreUpgradeStats = dictionary }; } [HarmonyPostfix] public static void Postfix(ItemUpgrade __instance, UpgradeContext __state) { if (!SemiFunc.IsMasterClientOrSingleplayer() || string.IsNullOrEmpty(__state.SteamID) || (Object)(object)PunManager.instance == (Object)null) { return; } PhotonView component = ((Component)PunManager.instance).GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogError((object)"PunManager PhotonView not found."); return; } SortedDictionary> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance); if (dictOfDicts == null) { return; } foreach (KeyValuePair> item in dictOfDicts) { if (item.Key.StartsWith("playerUpgrade") && StatsManagerInitPatch.VanillaKeys.Contains(item.Key)) { int value; int num = (item.Value.TryGetValue(__state.SteamID, out value) ? value : 0); int value2; int num2 = (__state.PreUpgradeStats.TryGetValue(item.Key, out value2) ? value2 : 0); if (num > num2) { int num3 = num - num2; string command = item.Key.Substring("playerUpgrade".Length); Plugin.Log.LogInfo((object)$"Upgrade detected: {item.Key} (+{num3}) for {__state.PlayerName}"); DistributeUpgrade(component, command, num3, __state); } } } } private static void DistributeUpgrade(PhotonView punView, string command, int amount, UpgradeContext context) { foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.photonView == (Object)null) && item.photonView.ViewID != context.ViewID) { string text = (string)AccessTools.Field(typeof(PlayerAvatar), "steamID").GetValue(item); if (!string.IsNullOrEmpty(text)) { punView.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { text, command, amount }); Plugin.Log.LogInfo((object)("Synced " + command + " to " + text)); } } } } } [HarmonyPatch(typeof(PlayerAvatar), "Start")] [HarmonyWrapSafe] public static class LateJoinSyncPatch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PlayerAvatar newPlayer; private string 5__2; private float 5__3; private float 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if ((Object)(object)newPlayer == (Object)null) { return false; } 5__2 = SemiFunc.PlayerGetSteamID(newPlayer); if (string.IsNullOrEmpty(5__2)) { 5__4 = 0f; goto IL_00a7; } goto IL_00df; case 1: <>1__state = -1; 5__4 += 0.1f; 5__2 = SemiFunc.PlayerGetSteamID(newPlayer); goto IL_00a7; case 2: <>1__state = -1; 5__3 += 0.2f; goto IL_0132; case 3: { <>1__state = -1; PlayerAvatar val = ((IEnumerable)SemiFunc.PlayerGetAll()).FirstOrDefault((Func)((PlayerAvatar p) => (Object)(object)p != (Object)null && (Object)(object)p.photonView != (Object)null && p.photonView.IsMine)); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("Late-join sync for " + 5__2 + ": host avatar not found.")); return false; } string text = SemiFunc.PlayerGetSteamID(val); if (string.IsNullOrEmpty(text)) { Plugin.Log.LogWarning((object)("Late-join sync for " + 5__2 + ": host SteamID empty.")); return false; } if (5__2 == text) { CrewUpgradesPatch.SyncedPlayers.Add(5__2); Plugin.Log.LogInfo((object)"Late-join sync: skipping host (self)."); return false; } PhotonView component = ((Component)PunManager.instance).GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogError((object)("Late-join sync for " + 5__2 + ": PunManager PhotonView not found.")); return false; } SortedDictionary> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance); if (dictOfDicts == null) { Plugin.Log.LogError((object)("Late-join sync for " + 5__2 + ": could not access StatsManager dictionaries.")); return false; } int num = 0; foreach (string vanillaKey in StatsManagerInitPatch.VanillaKeys) { if (dictOfDicts.TryGetValue(vanillaKey, out var value)) { int value2; int num2 = (value.TryGetValue(text, out value2) ? value2 : 0); int value3; int num3 = (value.TryGetValue(5__2, out value3) ? value3 : 0); int num4 = num2 - num3; if (num4 != 0) { string text2 = vanillaKey.Substring("playerUpgrade".Length); component.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { 5__2, text2, num4 }); num++; Plugin.Log.LogInfo((object)string.Format("Late-join sync: {0} for {1} ({2}{3}, host has {4}, target had {5})", text2, 5__2, (num4 > 0) ? "+" : "", num4, num2, num3)); } } } CrewUpgradesPatch.SyncedPlayers.Add(5__2); Plugin.Log.LogInfo((object)$"Late-join sync complete for {5__2}: {num} upgrade(s) reconciled to match host."); return false; } IL_00df: if (CrewUpgradesPatch.SyncedPlayers.Contains(5__2)) { return false; } 5__3 = 0f; goto IL_0132; IL_0132: if (((Object)(object)StatsManager.instance == (Object)null || (Object)(object)PunManager.instance == (Object)null) && 5__3 < 10f) { <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 2; return true; } if ((Object)(object)StatsManager.instance == (Object)null || (Object)(object)PunManager.instance == (Object)null) { Plugin.Log.LogWarning((object)("Late-join sync for " + 5__2 + ": managers never ready, aborting.")); return false; } <>2__current = (object)new WaitForSeconds(1f); <>1__state = 3; return true; IL_00a7: if (string.IsNullOrEmpty(5__2) && 5__4 < 5f) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; } if (string.IsNullOrEmpty(5__2)) { Plugin.Log.LogWarning((object)"Late-join sync: SteamID never populated, aborting."); return false; } goto IL_00df; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPostfix] public static void Postfix(PlayerAvatar __instance) { if (!((Object)(object)__instance == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer()) { ((MonoBehaviour)__instance).StartCoroutine(SyncWithDelay(__instance)); } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator SyncWithDelay(PlayerAvatar newPlayer) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0) { newPlayer = newPlayer }; } } [HarmonyPatch(typeof(SteamManager))] [HarmonyWrapSafe] public static class LobbyResetPatch { [HarmonyPatch("LeaveLobby")] [HarmonyPostfix] public static void LeaveLobby_Postfix() { if (CrewUpgradesPatch.SyncedPlayers.Count > 0) { Plugin.Log.LogInfo((object)$"Leaving lobby — clearing late-join sync state ({CrewUpgradesPatch.SyncedPlayers.Count} entries)."); CrewUpgradesPatch.SyncedPlayers.Clear(); } } [HarmonyPatch("OnLobbyMemberLeft")] [HarmonyPostfix] public static void OnLobbyMemberLeft_Postfix(Friend _friend) { string text = _friend.Id.Value.ToString(); if (!string.IsNullOrEmpty(text) && CrewUpgradesPatch.SyncedPlayers.Remove(text)) { Plugin.Log.LogInfo((object)("Member left (" + ((Friend)(ref _friend)).Name + ") — removed from synced set; will re-sync on rejoin.")); } } } [HarmonyPatch(typeof(StatsManager), "Start")] [HarmonyWrapSafe] public static class StatsManagerInitPatch { public static HashSet VanillaKeys = new HashSet(); private static readonly FieldInfo DictOfDictsField = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries"); public static SortedDictionary>? GetDictOfDicts(StatsManager sm) { if ((Object)(object)sm == (Object)null || DictOfDictsField == null) { return null; } return DictOfDictsField.GetValue(sm) as SortedDictionary>; } [HarmonyPostfix] public static void Postfix(StatsManager __instance) { VanillaKeys.Clear(); SortedDictionary> dictOfDicts = GetDictOfDicts(__instance); if (dictOfDicts == null) { Plugin.Log.LogError((object)"Could not access StatsManager.dictionaryOfDictionaries via reflection."); return; } HashSet hashSet = (from f in typeof(StatsManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) select f.Name).ToHashSet(); foreach (string key in dictOfDicts.Keys) { if (key.StartsWith("playerUpgrade") && hashSet.Contains(key)) { VanillaKeys.Add(key); } } Plugin.Log.LogInfo((object)$"Discovered {VanillaKeys.Count} vanilla upgrade keys."); } } }