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);
}
}
}
}
}