Decompiled source of BlueSagePatched PlayerLimitLift v1.1.1

BepInEx\plugins\BlueSagePatched_PlayerLimitLift\BlueSagePatched_PlayerLimitLift.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PurrLobby;
using PurrNet;
using PurrNet.Modules;
using PurrNet.Packing;
using PurrNet.Transports;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("BlueSagePatched_PlayerLimitLift")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+beb00bc563a9599284cac5ddf8e96c64020715b1")]
[assembly: AssemblyProduct("BlueSagePatched_PlayerLimitLift")]
[assembly: AssemblyTitle("BlueSagePatched_PlayerLimitLift")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[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 BlueSagePatched.PlayerLimitLift
{
	[BepInPlugin("com.bluesage.ontogether.playerlimitlift", "BlueSagePatched PlayerLimitLift", "1.1.1")]
	[BepInProcess("OnTogether.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.bluesage.ontogether.playerlimitlift";

		public const string PluginName = "BlueSagePatched PlayerLimitLift";

		public const string PluginVersion = "1.1.1";

		private readonly Harmony _harmony = new Harmony("com.bluesage.ontogether.playerlimitlift");

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<int> DefaultLobbySize { get; private set; }

		internal static ConfigEntry<int> MaxLobbySize { get; private set; }

		internal static ConfigEntry<int> ShiftSkipRate { get; private set; }

		internal static ConfigEntry<bool> EnableChatRelayPatch { get; private set; }

		internal static int EffectiveMaxLobbySize => Clamp(MaxLobbySize.Value, 16, 128);

		internal static int EffectiveDefaultLobbySize => Clamp(DefaultLobbySize.Value, 1, EffectiveMaxLobbySize);

		internal static int EffectiveShiftSkipRate => Clamp(ShiftSkipRate.Value, 1, EffectiveMaxLobbySize);

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			_harmony.PatchAll();
			Log.LogInfo((object)string.Format("{0} {1} loaded. Lobby cap={2}, default={3}.", "BlueSagePatched PlayerLimitLift", "1.1.1", EffectiveMaxLobbySize, EffectiveDefaultLobbySize));
			LogStartupDiagnostics();
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"BlueSagePatched PlayerLimitLift unloaded and Harmony patches removed.");
			}
		}

		private void BindConfig()
		{
			DefaultLobbySize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "DefaultLobbySize", 16, "Lobby size shown on launch. Clamped between 1 and MaxLobbySize. Takes effect after restart.");
			MaxLobbySize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxLobbySize", 128, "Maximum lobby size exposed by this replacement mod. On-Together currently works best at 128 or lower.");
			ShiftSkipRate = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ShiftSkipRate", 16, "Amount that shift+click changes lobby size by. Clamped between 1 and MaxLobbySize.");
			EnableChatRelayPatch = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableChatRelayPatch", true, "Keeps chat functional when sender indexes exceed the vanilla 16-player color/name assumptions.");
		}

		private static void LogStartupDiagnostics()
		{
			WarnIfClamped("DefaultLobbySize", DefaultLobbySize.Value, EffectiveDefaultLobbySize);
			WarnIfClamped("MaxLobbySize", MaxLobbySize.Value, EffectiveMaxLobbySize);
			WarnIfClamped("ShiftSkipRate", ShiftSkipRate.Value, EffectiveShiftSkipRate);
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				if (((value != null) ? value.Metadata : null) != null && !string.Equals(value.Metadata.GUID, "com.bluesage.ontogether.playerlimitlift", StringComparison.OrdinalIgnoreCase))
				{
					string text = (value.Metadata.GUID + " " + value.Metadata.Name).Trim();
					string text2 = text.ToLowerInvariant();
					bool flag = text2.Contains("playerlimit") || text2.Contains("player limit");
					bool flag2 = text2.Contains("officerballs") || text2.Contains("officer_balls");
					if (flag && flag2)
					{
						Log.LogWarning((object)("Old officerballs PlayerLimitLift appears loaded (" + text + "). Remove or disable it so BlueSage PlayerLimitLift is the only PlayerLimitLift mod on the host; old client copies are the common NickName1/chat-glitch cause."));
					}
					else if (flag)
					{
						Log.LogWarning((object)("Another PlayerLimitLift-like plugin appears loaded (" + text + "). Hosts should run only one lobby-size mod at a time."));
					}
				}
			}
		}

		private static void WarnIfClamped(string name, int configured, int effective)
		{
			if (configured != effective)
			{
				Log.LogWarning((object)string.Format("{0}={1} was clamped to {2}. Check BepInEx/config/{3}.cfg if this was not intentional.", name, configured, effective, "com.bluesage.ontogether.playerlimitlift"));
			}
		}

		internal static int WrapLobbySize(int current, int delta)
		{
			int effectiveMaxLobbySize = EffectiveMaxLobbySize;
			return Mod(current - 1 + delta, effectiveMaxLobbySize) + 1;
		}

		internal static int WrapLobbyListFilter(int current, int delta)
		{
			int modulus = EffectiveMaxLobbySize + 1;
			return Mod(current + delta, modulus);
		}

		internal static int ClampSenderIndexForColor(int senderIndex)
		{
			return Clamp(senderIndex, 0, 15);
		}

		private static int Clamp(int value, int min, int max)
		{
			if (value < min)
			{
				return min;
			}
			if (value > max)
			{
				return max;
			}
			return value;
		}

		private static int Mod(int value, int modulus)
		{
			int num = value % modulus;
			if (num >= 0)
			{
				return num;
			}
			return num + modulus;
		}
	}
}
namespace BlueSagePatched.PlayerLimitLift.Patches
{
	[HarmonyPatch(typeof(PlayerPanelController))]
	internal static class PanelPatch
	{
		[HarmonyPatch("UpdateServerPanel")]
		[HarmonyPostfix]
		private static void SizeMarker()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			LobbyManager val = MonoSingleton<MultiplayerManager>.I?._lobbyManager;
			if (val == null)
			{
				return;
			}
			_ = val.CurrentLobby;
			if (0 == 0 && !((Object)(object)NetworkSingleton<PlayerPanelController>.I == (Object)null))
			{
				TextMeshProUGUI val2 = AccessTools.FieldRefAccess<PlayerPanelController, TextMeshProUGUI>("_lobbyNameText").Invoke(NetworkSingleton<PlayerPanelController>.I);
				if (!((Object)(object)val2 == (Object)null))
				{
					int num = val.CurrentLobby.MaxPlayers - 1;
					int count = val.CurrentLobby.Members.Count;
					((TMP_Text)val2).text = $"{MonoSingleton<MultiplayerManager>.I.LobbyName} ({count}/{num})";
				}
			}
		}
	}
	[HarmonyPatch(typeof(MainMenuUIController))]
	internal static class PlayerLimitPatch
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePatch(ref TextMeshProUGUI ____filterPlayerText)
		{
			MonoSingleton<MultiplayerManager>.I.FilterPlayerValue = Plugin.EffectiveDefaultLobbySize;
			if ((Object)(object)____filterPlayerText != (Object)null)
			{
				((TMP_Text)____filterPlayerText).text = Plugin.EffectiveDefaultLobbySize.ToString();
			}
		}

		[HarmonyPatch("ButtonChangeFilterPlayer")]
		[HarmonyPrefix]
		private static bool ButtonChangeFilterPlayerPatch(int value, ref TextMeshProUGUI ____filterPlayerText)
		{
			int num = ((!IsShiftHeld()) ? 1 : Plugin.EffectiveShiftSkipRate);
			int delta = ((value == 1) ? num : (-num));
			int filterPlayerValue = Plugin.WrapLobbySize(MonoSingleton<MultiplayerManager>.I.FilterPlayerValue, delta);
			MonoSingleton<MultiplayerManager>.I.FilterPlayerValue = filterPlayerValue;
			if ((Object)(object)____filterPlayerText != (Object)null)
			{
				((TMP_Text)____filterPlayerText).text = filterPlayerValue.ToString();
			}
			return false;
		}

		[HarmonyPatch("ButtonChangeFilterListPlayer")]
		[HarmonyPrefix]
		private static bool ButtonChangeFilterListPlayerPatch(int value, ref TextMeshProUGUI ____filterListPlayerText)
		{
			int num = ((!IsShiftHeld()) ? 1 : Plugin.EffectiveShiftSkipRate);
			int delta = ((value == 1) ? num : (-num));
			int num2 = Plugin.WrapLobbyListFilter(MonoSingleton<MultiplayerManager>.I.FilterListPlayerValue, delta);
			MonoSingleton<MultiplayerManager>.I.FilterListPlayerValue = num2;
			if ((Object)(object)____filterListPlayerText != (Object)null)
			{
				((TMP_Text)____filterListPlayerText).text = ((num2 == 0) ? MonoSingleton<SettingsController>.I.AllString.String : num2.ToString());
			}
			return false;
		}

		private static bool IsShiftHeld()
		{
			if (!Input.GetKey((KeyCode)304))
			{
				return Input.GetKey((KeyCode)303);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MultiplayerManager))]
	internal static class ServerTagPatch
	{
		[HarmonyPatch("CreateLobby")]
		[HarmonyPrefix]
		private static void MarkCreatedLobbyAsModded(ref List<bool> ___FilterSocialTags)
		{
			if (___FilterSocialTags != null)
			{
				while (___FilterSocialTags.Count <= 4)
				{
					___FilterSocialTags.Add(item: false);
				}
				___FilterSocialTags[4] = true;
			}
		}
	}
	[HarmonyPatch(typeof(TextChannelManager))]
	internal static class TextChannelManagerPatch
	{
		private static readonly MethodInfo OnChannelMessageReceivedMethod = AccessTools.Method(typeof(TextChannelManager), "OnChannelMessageReceived", (Type[])null, (Type[])null);

		private static readonly MethodInfo AddMessageUiMethod = AccessTools.Method(typeof(TextChannelManager), "AddMessageUI", (Type[])null, (Type[])null);

		[HarmonyPatch(typeof(TextChannelManager), "SendMessageAsync_Original_0")]
		[HarmonyPrefix]
		private static bool SendMessagePatch(TextChannelManager __instance, ref byte[] textBytes, ref byte[] userName, ref bool isLocal, ref Vector3 pos, ref string playerID, ref RPCInfo info, ref string ____playerId)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.EnableChatRelayPatch.Value)
			{
				return true;
			}
			string message = Encoding.Unicode.GetString(textBytes);
			string senderName = Encoding.Unicode.GetString(userName);
			PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
			int colorIndex = Plugin.ClampSenderIndexForColor(FindSenderIndex(i, info.sender, playerID));
			if (!IsFromLocalPlayer(__instance, info.sender))
			{
				InvokeOnChannelMessageReceived(__instance, senderName, message, pos, isLocal, colorIndex, playerID);
				return false;
			}
			if (playerID != ____playerId)
			{
				InvokeAddMessageUi(__instance, senderName, message, isLocal, colorIndex);
				ShowRemoteTextBubble(i, textBytes, isLocal, pos, playerID);
				return false;
			}
			InvokeAddMessageUi(__instance, senderName, message, isLocal, colorIndex);
			TextBubbleController mainTextBubble = __instance.MainTextBubble;
			if (mainTextBubble != null)
			{
				mainTextBubble.ShowTextBubble(textBytes, isLocal, pos, playerID);
			}
			return false;
		}

		[HarmonyPatch(typeof(TextChannelManager), "OnReceivedRpc")]
		[HarmonyPrefix]
		private static bool RPCInterceptPatcher(ref int id, ref RPCInfo info, ref RPCPacket packet, ref bool asServer, TextChannelManager __instance)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.EnableChatRelayPatch.Value)
			{
				return true;
			}
			if (id != 0)
			{
				return true;
			}
			if (asServer && FindSenderIndex(NetworkSingleton<PlayerPanelController>.I, info.sender, null) > 15)
			{
				if (TryReadSendMessagePacket(packet, info, out var textBytes, out var userName, out var isLocal, out var pos, out var playerID))
				{
					__instance.SendMessageAsync(textBytes, userName, isLocal, pos, playerID, default(RPCInfo));
				}
				return false;
			}
			__instance.HandleRPCGenerated_0(packet, info, asServer);
			return false;
		}

		private unsafe static bool TryReadSendMessagePacket(RPCPacket packet, RPCInfo info, out byte[] textBytes, out byte[] userName, out bool isLocal, out Vector3 pos, out string playerID)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			textBytes = null;
			userName = null;
			isLocal = false;
			pos = Vector3.zero;
			playerID = null;
			try
			{
				info.compileTimeSignature = RPCSignature.Make((RPCType)1, (Channel)2, false, false, false, true, false, "SendMessageAsync", false, 5f, (CompressionLevel)0, false, false);
				BitData data = packet.data;
				RPCModule.PostProcessRpc(info, ref data);
				BitDataScope val = ((BitData)(ref data)).AutoScope();
				try
				{
					BitPacker packer = data.packer;
					Packer<byte[]>.Read(packer, ref textBytes);
					Packer<byte[]>.Read(packer, ref userName);
					Packer<bool>.Read(packer, ref isLocal);
					Packer<Vector3>.Read(packer, ref pos);
					Packer<string>.Read(packer, ref playerID);
				}
				finally
				{
					((IDisposable)(*(BitDataScope*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to decode oversized-lobby chat RPC; dropping packet to avoid corrupt chat state. " + ex.GetType().Name + ": " + ex.Message));
				}
				return false;
			}
		}

		private static int FindSenderIndex(PlayerPanelController panel, PlayerID sender, string playerID)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)panel == (Object)null)
			{
				return 0;
			}
			List<PlayerID> playerIDs = panel.PlayerIDs;
			for (int i = 0; i < playerIDs.Count; i++)
			{
				if (sender == playerIDs[i])
				{
					return i;
				}
			}
			if (!string.IsNullOrEmpty(playerID))
			{
				int num = panel.PlayerSteamIDs.IndexOf(playerID);
				if (num >= 0)
				{
					return num;
				}
			}
			return 0;
		}

		private static bool IsFromLocalPlayer(TextChannelManager instance, PlayerID sender)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			PlayerID? localPlayer = ((NetworkIdentity)instance).localPlayer;
			if (localPlayer.HasValue)
			{
				return sender == localPlayer.GetValueOrDefault();
			}
			return false;
		}

		private static void InvokeOnChannelMessageReceived(TextChannelManager instance, string senderName, string message, Vector3 pos, bool isLocal, int colorIndex, string playerID)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			OnChannelMessageReceivedMethod.Invoke(instance, new object[6] { senderName, message, pos, isLocal, colorIndex, playerID });
		}

		private static void InvokeAddMessageUi(TextChannelManager instance, string senderName, string message, bool isLocal, int colorIndex)
		{
			AddMessageUiMethod.Invoke(instance, new object[4] { senderName, message, isLocal, colorIndex });
		}

		private static void ShowRemoteTextBubble(PlayerPanelController panel, byte[] textBytes, bool isLocal, Vector3 pos, string playerID)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)panel == (Object)null)
			{
				return;
			}
			int num = panel.PlayerSteamIDs.IndexOf(playerID);
			if (num < 0 || num >= panel.PlayerTransforms.Count)
			{
				return;
			}
			NetworkTransform val = panel.PlayerTransforms[num];
			if (!((Object)(object)val == (Object)null))
			{
				TextBubbleController componentInChildren = ((Component)val).GetComponentInChildren<TextBubbleController>();
				if (componentInChildren != null)
				{
					componentInChildren.ShowTextBubble(textBytes, isLocal, pos, playerID);
				}
			}
		}
	}
}