Decompiled source of CrewUpgrades v1.0.2

plugins/CrewUpgrades/CrewUpgrades.dll

Decompiled 11 minutes ago
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<string, int> PreUpgradeStats;
		}

		public static HashSet<string> SyncedPlayers = new HashSet<string>();

		[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<string, int> dictionary = new Dictionary<string, int>();
			SortedDictionary<string, Dictionary<string, int>> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance);
			if (dictOfDicts != null)
			{
				foreach (KeyValuePair<string, Dictionary<string, int>> 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<PhotonView>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogError((object)"PunManager PhotonView not found.");
				return;
			}
			SortedDictionary<string, Dictionary<string, int>> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance);
			if (dictOfDicts == null)
			{
				return;
			}
			foreach (KeyValuePair<string, Dictionary<string, int>> 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 <SyncWithDelay>d__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerAvatar newPlayer;

			private string <steamID>5__2;

			private float <timeWaited>5__3;

			private float <waitedForID>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SyncWithDelay>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<steamID>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;
					}
					<steamID>5__2 = SemiFunc.PlayerGetSteamID(newPlayer);
					if (string.IsNullOrEmpty(<steamID>5__2))
					{
						<waitedForID>5__4 = 0f;
						goto IL_00a7;
					}
					goto IL_00df;
				case 1:
					<>1__state = -1;
					<waitedForID>5__4 += 0.1f;
					<steamID>5__2 = SemiFunc.PlayerGetSteamID(newPlayer);
					goto IL_00a7;
				case 2:
					<>1__state = -1;
					<timeWaited>5__3 += 0.2f;
					goto IL_0132;
				case 3:
					{
						<>1__state = -1;
						PlayerAvatar val = ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetAll()).FirstOrDefault((Func<PlayerAvatar, bool>)((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 " + <steamID>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 " + <steamID>5__2 + ": host SteamID empty."));
							return false;
						}
						if (<steamID>5__2 == text)
						{
							CrewUpgradesPatch.SyncedPlayers.Add(<steamID>5__2);
							Plugin.Log.LogInfo((object)"Late-join sync: skipping host (self).");
							return false;
						}
						PhotonView component = ((Component)PunManager.instance).GetComponent<PhotonView>();
						if ((Object)(object)component == (Object)null)
						{
							Plugin.Log.LogError((object)("Late-join sync for " + <steamID>5__2 + ": PunManager PhotonView not found."));
							return false;
						}
						SortedDictionary<string, Dictionary<string, int>> dictOfDicts = StatsManagerInitPatch.GetDictOfDicts(StatsManager.instance);
						if (dictOfDicts == null)
						{
							Plugin.Log.LogError((object)("Late-join sync for " + <steamID>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(<steamID>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] { <steamID>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, <steamID>5__2, (num4 > 0) ? "+" : "", num4, num2, num3));
								}
							}
						}
						CrewUpgradesPatch.SyncedPlayers.Add(<steamID>5__2);
						Plugin.Log.LogInfo((object)$"Late-join sync complete for {<steamID>5__2}: {num} upgrade(s) reconciled to match host.");
						return false;
					}
					IL_00df:
					if (CrewUpgradesPatch.SyncedPlayers.Contains(<steamID>5__2))
					{
						return false;
					}
					<timeWaited>5__3 = 0f;
					goto IL_0132;
					IL_0132:
					if (((Object)(object)StatsManager.instance == (Object)null || (Object)(object)PunManager.instance == (Object)null) && <timeWaited>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 " + <steamID>5__2 + ": managers never ready, aborting."));
						return false;
					}
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 3;
					return true;
					IL_00a7:
					if (string.IsNullOrEmpty(<steamID>5__2) && <waitedForID>5__4 < 5f)
					{
						<>2__current = (object)new WaitForSeconds(0.1f);
						<>1__state = 1;
						return true;
					}
					if (string.IsNullOrEmpty(<steamID>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(<SyncWithDelay>d__1))]
		private static IEnumerator SyncWithDelay(PlayerAvatar newPlayer)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SyncWithDelay>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<string> VanillaKeys = new HashSet<string>();

		private static readonly FieldInfo DictOfDictsField = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries");

		public static SortedDictionary<string, Dictionary<string, int>>? GetDictOfDicts(StatsManager sm)
		{
			if ((Object)(object)sm == (Object)null || DictOfDictsField == null)
			{
				return null;
			}
			return DictOfDictsField.GetValue(sm) as SortedDictionary<string, Dictionary<string, int>>;
		}

		[HarmonyPostfix]
		public static void Postfix(StatsManager __instance)
		{
			VanillaKeys.Clear();
			SortedDictionary<string, Dictionary<string, int>> dictOfDicts = GetDictOfDicts(__instance);
			if (dictOfDicts == null)
			{
				Plugin.Log.LogError((object)"Could not access StatsManager.dictionaryOfDictionaries via reflection.");
				return;
			}
			HashSet<string> 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.");
		}
	}
}