Decompiled source of MorePlayersCrossplay v1.2.0

BepInEx/plugins/MorePlayersCrossplay/ContentWarningHostOnlyLobby.dll

Decompiled 11 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DefaultNamespace;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ContentWarningHostOnlyLobby")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+34b643bbda542a68bca944b7e0033ffeb995a662")]
[assembly: AssemblyProduct("ContentWarningHostOnlyLobby")]
[assembly: AssemblyTitle("ContentWarningHostOnlyLobby")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 ContentWarningHostOnlyLobby
{
	[ContentWarningPlugin("local.contentwarning.hostonlylobby", "1.2.0", true)]
	[BepInPlugin("local.contentwarning.hostonlylobby", "HostOnlyLobby", "1.2.0")]
	public sealed class HostOnlyLobbyPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "local.contentwarning.hostonlylobby";

		public const string PluginName = "HostOnlyLobby";

		public const string PluginVersion = "1.2.0";

		private Harmony? _harmony;

		private bool _wasInHostRoom;

		private int _lastSceneHandle = -1;

		private float _indicatorVisibleFrom;

		private float _indicatorVisibleUntil;

		private GUIStyle? _indicatorTitleStyle;

		private GUIStyle? _indicatorDetailStyle;

		internal static int MaxPlayers { get; private set; } = 8;

		internal static int RequiredSleepers { get; private set; } = 4;

		internal static bool ShowStatusIndicator { get; private set; } = true;

		internal static float IndicatorSeconds { get; private set; } = 12f;

		internal static ManualLogSource? Log { get; private set; }

		private void Awake()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigEntry<int> obj = ((BaseUnityPlugin)this).Config.Bind<int>("Lobby", "MaxPlayers", 8, "Maximum lobby size. Keep between 5 and 16 for cross-platform testing.");
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowStatusIndicator", true, "Show a status card when the host enters a room or loads a world.");
			ConfigEntry<float> val2 = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "IndicatorSeconds", 12f, "How many seconds the status card remains visible. Keep between 3 and 30.");
			MaxPlayers = Clamp(obj.Value, 5, 16);
			RequiredSleepers = Math.Min(4, MaxPlayers);
			ShowStatusIndicator = val.Value;
			IndicatorSeconds = Mathf.Clamp(val2.Value, 3f, 30f);
			_harmony = new Harmony("local.contentwarning.hostonlylobby");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Lobby patches active. MaxPlayers={MaxPlayers}. Extra PC clients in slots 5+ also need this plugin for safe local spawning.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Late joining is intentionally disabled; have everyone join before opening the house door.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Spawn compatibility is active on this device for house, dive-bell return, and hospital spawns.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Status indicator enabled: {ShowStatusIndicator} ({IndicatorSeconds:0.#} seconds).");
			RunStartupDiagnostics();
		}

		private void Update()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient;
			Scene activeScene = SceneManager.GetActiveScene();
			int handle = ((Scene)(ref activeScene)).handle;
			if (ShowStatusIndicator && flag && (!_wasInHostRoom || handle != _lastSceneHandle))
			{
				_indicatorVisibleFrom = Time.realtimeSinceStartup + 1f;
				_indicatorVisibleUntil = _indicatorVisibleFrom + IndicatorSeconds;
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				activeScene = SceneManager.GetActiveScene();
				logger.LogInfo((object)("Status indicator scheduled in scene '" + ((Scene)(ref activeScene)).name + "'."));
			}
			_wasInHostRoom = flag;
			_lastSceneHandle = handle;
		}

		private void OnGUI()
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			if (ShowStatusIndicator && !(Time.realtimeSinceStartup < _indicatorVisibleFrom) && !(Time.realtimeSinceStartup > _indicatorVisibleUntil))
			{
				EnsureIndicatorStyles();
				float num = Mathf.Min(340f, (float)Screen.width - 32f);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)Screen.width - num - 16f, 18f, num, 72f);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, 5f, ((Rect)(ref val)).height);
				Rect val3 = default(Rect);
				((Rect)(ref val3))..ctor(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 28f, 24f);
				Rect val4 = new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 36f, ((Rect)(ref val)).width - 28f, 24f);
				Color color = GUI.color;
				GUI.color = new Color(0.055f, 0.071f, 0.102f, 0.94f);
				GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = new Color(0.25f, 0.88f, 0.55f, 1f);
				GUI.DrawTexture(val2, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = Color.white;
				GUI.Label(val3, "HOSTONLYLOBBY ATIVO", _indicatorTitleStyle);
				GUI.Label(val4, $"Sala do host  •  até {MaxPlayers} jogadores", _indicatorDetailStyle);
				GUI.color = color;
			}
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Log = null;
		}

		private void EnsureIndicatorStyles()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (_indicatorTitleStyle == null || _indicatorDetailStyle == null)
			{
				_indicatorTitleStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 15,
					fontStyle = (FontStyle)1
				};
				_indicatorTitleStyle.normal.textColor = Color.white;
				_indicatorDetailStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 12
				};
				_indicatorDetailStyle.normal.textColor = new Color(0.78f, 0.82f, 0.88f, 1f);
			}
		}

		private static int Clamp(int value, int minimum, int maximum)
		{
			if (value < minimum)
			{
				return minimum;
			}
			if (value > maximum)
			{
				return maximum;
			}
			return value;
		}

		internal static void LogSpawnRemap(int originalIndex, int safeIndex)
		{
			ManualLogSource? log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Spawn compatibility remapped local slot {originalIndex} to vanilla spawn {safeIndex}.");
			}
		}

		private void RunStartupDiagnostics()
		{
			MethodBase[] array = new MethodBase[8]
			{
				AccessTools.Method(typeof(SteamLobbyHandler), "HostMatch", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(PhotonNetwork), "CreateRoom", new Type[4]
				{
					typeof(string),
					typeof(RoomOptions),
					typeof(TypedLobby),
					typeof(string[])
				}, (Type[])null),
				AccessTools.PropertyGetter(typeof(InviteFriendsTerminal), "IsGameFull"),
				AccessTools.Method(typeof(RichPresenceHandler), "SetGroupSize", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(BedBoss), "OnPlayerJoined", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(PlayerHandler), "AllPlayersInBed", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(PlayerHandler), "AllPlayersAsleep", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(SpawnHandler), "FindLocalSpawnIndex", (Type[])null, (Type[])null)
			};
			int num = 0;
			MethodBase[] array2 = array;
			foreach (MethodBase methodBase in array2)
			{
				Patches patchInfo = Harmony.GetPatchInfo(methodBase);
				bool flag = false;
				if (patchInfo != null)
				{
					foreach (string owner in patchInfo.Owners)
					{
						if (owner == "local.contentwarning.hostonlylobby")
						{
							flag = true;
							break;
						}
					}
				}
				if (flag)
				{
					num++;
					continue;
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Startup diagnostic failed: patch missing on " + methodBase.DeclaringType?.Name + "." + methodBase.Name + "."));
			}
			if (num == array.Length)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Startup diagnostics passed: {num}/{array.Length} host-side patches registered.");
			}
		}
	}
	internal static class HostOnlyLobbyRules
	{
		internal static byte PhotonRoomLimit => 0;

		internal static bool IsAtCapacity(int playerCount)
		{
			return playerCount >= HostOnlyLobbyPlugin.MaxPlayers;
		}

		internal static int RichPresenceMaximum(int requestedMaximum)
		{
			if (requestedMaximum > 4)
			{
				return requestedMaximum;
			}
			return HostOnlyLobbyPlugin.MaxPlayers;
		}

		internal static int BedAssignmentCount(int bedCount, int playerCount)
		{
			return Math.Min(4, Math.Min(bedCount, playerCount));
		}

		internal static bool EnoughPlayersReady(int alivePlayers, int readyPlayers)
		{
			int num = Math.Min(HostOnlyLobbyPlugin.RequiredSleepers, alivePlayers);
			if (num > 0)
			{
				return readyPlayers >= num;
			}
			return false;
		}

		internal static int SafeSpawnIndex(int originalIndex, int houseSpawnCount, int diveBellSpawnCount)
		{
			if (originalIndex <= 0)
			{
				return 0;
			}
			int num = Math.Min(houseSpawnCount, diveBellSpawnCount);
			if (num <= 1)
			{
				return 0;
			}
			return 1 + (originalIndex - 1) % (num - 1);
		}
	}
	[HarmonyPatch(typeof(SpawnHandler))]
	[HarmonyPriority(800)]
	internal static class SpawnHandlerPatches
	{
		private static readonly FieldInfo LocalSpawnIndexField = typeof(SpawnHandler).GetField("m_LocalSpawnIndex", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(SpawnHandler).FullName, "m_LocalSpawnIndex");

		private static readonly FieldInfo HouseSpawnsField = typeof(SpawnHandler).GetField("m_HouseSpawns", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(SpawnHandler).FullName, "m_HouseSpawns");

		private static readonly FieldInfo DiveBellSpawnsField = typeof(SpawnHandler).GetField("m_DiveBellSpawns", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(SpawnHandler).FullName, "m_DiveBellSpawns");

		[HarmonyPostfix]
		[HarmonyPatch("FindLocalSpawnIndex")]
		private static void FindLocalSpawnIndexPostfix(SpawnHandler __instance)
		{
			int num = (int)LocalSpawnIndexField.GetValue(__instance);
			Transform[] array = (Transform[])HouseSpawnsField.GetValue(__instance);
			Transform[] array2 = (Transform[])DiveBellSpawnsField.GetValue(__instance);
			int num2 = HostOnlyLobbyRules.SafeSpawnIndex(num, (array != null) ? array.Length : 0, (array2 != null) ? array2.Length : 0);
			if (num2 != num)
			{
				LocalSpawnIndexField.SetValue(__instance, num2);
				HostOnlyLobbyPlugin.LogSpawnRemap(num, num2);
			}
		}
	}
	[HarmonyPatch(typeof(SteamLobbyHandler))]
	internal static class SteamLobbyPatches
	{
		private static readonly FieldInfo MaxPlayersField = typeof(SteamLobbyHandler).GetField("m_MaxPlayers", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(SteamLobbyHandler).FullName, "m_MaxPlayers");

		[HarmonyPrefix]
		[HarmonyPatch("HostMatch")]
		private static void HostMatchPrefix(SteamLobbyHandler __instance)
		{
			MaxPlayersField.SetValue(__instance, HostOnlyLobbyPlugin.MaxPlayers);
		}
	}
	[HarmonyPatch(typeof(PhotonNetwork))]
	internal static class PhotonRoomPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("CreateRoom", new Type[]
		{
			typeof(string),
			typeof(RoomOptions),
			typeof(TypedLobby),
			typeof(string[])
		})]
		private static void CreateRoomPrefix(ref RoomOptions roomOptions)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			if (roomOptions == null)
			{
				roomOptions = new RoomOptions();
			}
			roomOptions.MaxPlayers = HostOnlyLobbyRules.PhotonRoomLimit;
		}
	}
	[HarmonyPatch(typeof(InviteFriendsTerminal))]
	internal static class InviteTerminalPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static bool IsGameFullPrefix(ref bool __result)
		{
			__result = (Object)(object)PlayerHandler.instance != (Object)null && HostOnlyLobbyRules.IsAtCapacity(PlayerHandler.instance.players.Count);
			return false;
		}
	}
	[HarmonyPatch(typeof(RichPresenceHandler))]
	internal static class RichPresencePatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("SetGroupSize")]
		private static void SetGroupSizePrefix(ref int maxSize)
		{
			maxSize = HostOnlyLobbyRules.RichPresenceMaximum(maxSize);
		}
	}
	[HarmonyPatch(typeof(BedBoss))]
	[HarmonyPriority(800)]
	internal static class BedPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("OnPlayerJoined")]
		private static bool OnPlayerJoinedPrefix(BedBoss __instance)
		{
			if (!PhotonNetwork.IsMasterClient || (Object)(object)PlayerHandler.instance == (Object)null)
			{
				return false;
			}
			int num = HostOnlyLobbyRules.BedAssignmentCount(__instance.beds.Count, PlayerHandler.instance.players.Count);
			PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
			for (int i = 0; i < num; i++)
			{
				Player val = PlayerHandler.instance.players[i];
				component.RPC("AssignBed", (RpcTarget)0, new object[2]
				{
					val.refs.view.ViewID,
					i
				});
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerHandler))]
	internal static class SleepPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("AllPlayersInBed")]
		private static void AllPlayersInBedPostfix(PlayerHandler __instance, ref bool __result)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < __instance.playersAlive.Count; i++)
			{
				if ((Object)(object)__instance.playersAlive[i].data.currentBed != (Object)null)
				{
					num++;
				}
			}
			__result = HostOnlyLobbyRules.EnoughPlayersReady(__instance.playersAlive.Count, num);
		}

		[HarmonyPostfix]
		[HarmonyPatch("AllPlayersAsleep")]
		private static void AllPlayersAsleepPostfix(PlayerHandler __instance, ref bool __result)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < __instance.playersAlive.Count; i++)
			{
				if (__instance.playersAlive[i].data.sleepAmount >= 0.9f)
				{
					num++;
				}
			}
			__result = HostOnlyLobbyRules.EnoughPlayersReady(__instance.playersAlive.Count, num);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}