Decompiled source of PlayerCountFixed v1.0.0

PlayerCountFixed.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[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("PlayerCountFixed")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PlayerCountFixed")]
[assembly: AssemblyTitle("PlayerCountFixed")]
[assembly: AssemblyVersion("1.0.0.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 PlayerCountFixed
{
	[BepInPlugin("local.playercountfixed", "PlayerCountFixed", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(MenuPageLobby), "Awake")]
		private static class MenuPageLobbyAwakePatch
		{
			private static void Postfix(MenuPageLobby __instance)
			{
				TryAttachLobbyCounter(__instance);
			}
		}

		[HarmonyPatch(typeof(MenuPageLobby), "Start")]
		private static class MenuPageLobbyStartPatch
		{
			private static void Postfix(MenuPageLobby __instance)
			{
				TryAttachLobbyCounter(__instance);
			}
		}

		private sealed class PlayerCountFixedUpdater : MonoBehaviour
		{
			[CompilerGenerated]
			private sealed class <RefreshLoop>d__7 : IEnumerator<object>, IEnumerator, IDisposable
			{
				private int <>1__state;

				private object <>2__current;

				public PlayerCountFixedUpdater <>4__this;

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

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

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

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					<>1__state = -2;
				}

				private bool MoveNext()
				{
					int num = <>1__state;
					PlayerCountFixedUpdater playerCountFixedUpdater = <>4__this;
					if (num != 0)
					{
						if (num != 1)
						{
							return false;
						}
						<>1__state = -1;
					}
					else
					{
						<>1__state = -1;
					}
					playerCountFixedUpdater.RefreshOnce();
					<>2__current = playerCountFixedUpdater._refreshDelay;
					<>1__state = 1;
					return true;
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}
			}

			private TextMeshProUGUI? _text;

			private Coroutine? _refreshCoroutine;

			private string? _lastText;

			private readonly WaitForSeconds _refreshDelay = new WaitForSeconds(0.25f);

			internal void SetText(TextMeshProUGUI text)
			{
				_text = text;
				if (_refreshCoroutine == null && ((Behaviour)this).isActiveAndEnabled)
				{
					_refreshCoroutine = ((MonoBehaviour)this).StartCoroutine(RefreshLoop());
				}
			}

			private void OnEnable()
			{
				if ((Object)(object)_text != (Object)null && _refreshCoroutine == null)
				{
					_refreshCoroutine = ((MonoBehaviour)this).StartCoroutine(RefreshLoop());
				}
			}

			private void OnDisable()
			{
				if (_refreshCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_refreshCoroutine);
					_refreshCoroutine = null;
				}
			}

			[IteratorStateMachine(typeof(<RefreshLoop>d__7))]
			private IEnumerator RefreshLoop()
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <RefreshLoop>d__7(0)
				{
					<>4__this = this
				};
			}

			private void RefreshOnce()
			{
				try
				{
					if (!((Object)(object)_text == (Object)null))
					{
						string text = string.Empty;
						Room currentRoom = PhotonNetwork.CurrentRoom;
						if (PhotonNetwork.InRoom && currentRoom != null)
						{
							int num = Instance?.ResolveMaxPlayers(currentRoom) ?? currentRoom.MaxPlayers;
							text = $"Players: {currentRoom.PlayerCount}/{num}";
						}
						if (!string.Equals(_lastText, text, StringComparison.Ordinal))
						{
							((TMP_Text)_text).text = text;
							_lastText = text;
						}
					}
				}
				catch (Exception ex)
				{
					Instance?.LogUpdateFailureOnce(ex);
				}
			}
		}

		private sealed class MaxPlayerProvider
		{
			private enum SupportedMod
			{
				None,
				MorePlayersSimple,
				MorePlayers,
				RoboUnion
			}

			private sealed class CachedConfigEntry
			{
				private readonly object _entry;

				private readonly PropertyInfo _valueProperty;

				private CachedConfigEntry(object entry, PropertyInfo valueProperty)
				{
					_entry = entry;
					_valueProperty = valueProperty;
				}

				internal static CachedConfigEntry? Create(object? entry)
				{
					if (entry == null)
					{
						return null;
					}
					PropertyInfo property = entry.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
					if (!(property != null))
					{
						return null;
					}
					return new CachedConfigEntry(entry, property);
				}

				internal int? ReadValue()
				{
					object value = _valueProperty.GetValue(_entry, null);
					if (!(value is int))
					{
						return null;
					}
					return (int)value;
				}
			}

			private readonly Plugin _plugin;

			private readonly SupportedMod _mod;

			private readonly PluginInfo? _pluginInfo;

			private bool _loggedFallback;

			private bool _lookupDisabled;

			private bool _morePlayersInitialized;

			private bool _morePlayersSimpleInitialized;

			private bool _roboUnionInitialized;

			private CachedConfigEntry? _morePlayersMax;

			private CachedConfigEntry? _simplePublicMax;

			private CachedConfigEntry? _simplePrivateMax;

			private CachedConfigEntry? _roboUnionMax;

			private MaxPlayerProvider(Plugin plugin, SupportedMod mod, PluginInfo? pluginInfo)
			{
				_plugin = plugin;
				_mod = mod;
				_pluginInfo = pluginInfo;
			}

			internal static MaxPlayerProvider Create(Plugin plugin)
			{
				PluginInfo value;
				bool flag = Chainloader.PluginInfos.TryGetValue("dyxc666.MorePlayersSimple", out value);
				PluginInfo value2;
				bool flag2 = Chainloader.PluginInfos.TryGetValue("ozoka.moreplayers", out value2);
				PluginInfo value3;
				bool flag3 = Chainloader.PluginInfos.TryGetValue("Linkoid.Repo.RoboUnion", out value3);
				if (flag)
				{
					((BaseUnityPlugin)plugin).Logger.LogInfo((object)BuildLoadedModLog("MorePlayersSimple", flag, flag2, flag3));
					return new MaxPlayerProvider(plugin, SupportedMod.MorePlayersSimple, value);
				}
				if (flag2)
				{
					((BaseUnityPlugin)plugin).Logger.LogInfo((object)BuildLoadedModLog("MorePlayers", flag, flag2, flag3));
					return new MaxPlayerProvider(plugin, SupportedMod.MorePlayers, value2);
				}
				if (flag3)
				{
					((BaseUnityPlugin)plugin).Logger.LogInfo((object)BuildLoadedModLog("RoboUnion", flag, flag2, flag3));
					return new MaxPlayerProvider(plugin, SupportedMod.RoboUnion, value3);
				}
				((BaseUnityPlugin)plugin).Logger.LogInfo((object)"No supported multiplayer mod is loaded; using Photon room max players.");
				return new MaxPlayerProvider(plugin, SupportedMod.None, null);
			}

			internal int? TryGetMaxPlayers(Room room)
			{
				if (_lookupDisabled)
				{
					return null;
				}
				return _mod switch
				{
					SupportedMod.MorePlayersSimple => GetMorePlayersSimpleMax(room), 
					SupportedMod.MorePlayers => GetMorePlayersMax(), 
					SupportedMod.RoboUnion => GetRoboUnionMax(), 
					_ => null, 
				};
			}

			private int? GetMorePlayersMax()
			{
				EnsureMorePlayersInitialized();
				int? num = _morePlayersMax?.ReadValue();
				LogFallbackIfNeeded(num, "MorePlayers");
				return num;
			}

			private int? GetMorePlayersSimpleMax(Room room)
			{
				EnsureMorePlayersSimpleInitialized();
				int? num = (room.IsVisible ? _simplePublicMax : _simplePrivateMax)?.ReadValue();
				LogFallbackIfNeeded(num, "MorePlayersSimple");
				return num;
			}

			private int? GetRoboUnionMax()
			{
				EnsureRoboUnionInitialized();
				int? num = _roboUnionMax?.ReadValue();
				if (num.HasValue && num.Value <= 0)
				{
					return 6;
				}
				LogFallbackIfNeeded(num, "RoboUnion");
				return num;
			}

			private void EnsureMorePlayersInitialized()
			{
				if (_morePlayersInitialized)
				{
					return;
				}
				try
				{
					Type type = FindType("MorePlayers.Plugin");
					_morePlayersMax = GetConfigEntryFromMember(type, "MaxPlayers") ?? GetConfigEntry("General", "Max Players");
				}
				catch (Exception)
				{
					_lookupDisabled = true;
					throw;
				}
				finally
				{
					_morePlayersInitialized = true;
				}
			}

			private void EnsureMorePlayersSimpleInitialized()
			{
				if (_morePlayersSimpleInitialized)
				{
					return;
				}
				try
				{
					Type type = FindType("MorePlayersSimple.MorePlayersSimplePlugin");
					_simplePublicMax = GetConfigEntryFromMember(type, "MaxPlayersPublic") ?? GetConfigEntry("General", "MaxPlayersPublic");
					_simplePrivateMax = GetConfigEntryFromMember(type, "MaxPlayersPrivate") ?? GetConfigEntry("General", "MaxPlayersPrivate");
				}
				catch (Exception)
				{
					_lookupDisabled = true;
					throw;
				}
				finally
				{
					_morePlayersSimpleInitialized = true;
				}
			}

			private void EnsureRoboUnionInitialized()
			{
				if (_roboUnionInitialized)
				{
					return;
				}
				try
				{
					object staticMemberValue = GetStaticMemberValue(FindType("Linkoid.Repo.RoboUnion.RoboUnion"), "ConfigModel");
					_roboUnionMax = GetConfigEntryFromMember(staticMemberValue?.GetType(), "MaxPlayers", staticMemberValue) ?? GetConfigEntry("General", "MaxPlayers");
				}
				catch (Exception)
				{
					_lookupDisabled = true;
					throw;
				}
				finally
				{
					_roboUnionInitialized = true;
				}
			}

			private Type? FindType(string typeName)
			{
				PluginInfo? pluginInfo = _pluginInfo;
				return (((Object)(object)((pluginInfo != null) ? pluginInfo.Instance : null) != (Object)null) ? ((object)_pluginInfo.Instance).GetType().Assembly : null)?.GetType(typeName, throwOnError: false) ?? Type.GetType(typeName, throwOnError: false);
			}

			private static CachedConfigEntry? GetConfigEntryFromMember(Type? type, string memberName, object? instance = null)
			{
				return CachedConfigEntry.Create(GetMemberValue(type, memberName, instance, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
			}

			private static object? GetStaticMemberValue(Type? type, string memberName)
			{
				return GetMemberValue(type, memberName, null, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}

			private static object? GetMemberValue(Type? type, string memberName, object? instance, BindingFlags flags)
			{
				if (type == null)
				{
					return null;
				}
				FieldInfo field = type.GetField(memberName, flags);
				if (field != null)
				{
					return field.GetValue(instance);
				}
				return type.GetProperty(memberName, flags)?.GetValue(instance, null);
			}

			private CachedConfigEntry? GetConfigEntry(string section, string key)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Expected O, but got Unknown
				PluginInfo? pluginInfo = _pluginInfo;
				object obj;
				if (pluginInfo == null)
				{
					obj = null;
				}
				else
				{
					BaseUnityPlugin instance = pluginInfo.Instance;
					obj = ((instance != null) ? instance.Config : null);
				}
				ConfigFile val = (ConfigFile)obj;
				if (val == null)
				{
					return null;
				}
				ConfigDefinition val2 = new ConfigDefinition(section, key);
				ConfigEntry<int> entry = default(ConfigEntry<int>);
				if (!val.TryGetEntry<int>(val2, ref entry))
				{
					return null;
				}
				return CachedConfigEntry.Create(entry);
			}

			private void LogFallbackIfNeeded(int? value, string modName)
			{
				if (!value.HasValue && !_loggedFallback)
				{
					((BaseUnityPlugin)_plugin).Logger.LogWarning((object)("Could not resolve " + modName + " configured max players; using Photon room max players."));
					_loggedFallback = true;
				}
			}

			private static string BuildLoadedModLog(string selected, bool simple, bool more, bool robo)
			{
				if ((simple ? 1 : 0) + (more ? 1 : 0) + (robo ? 1 : 0) <= 1)
				{
					return "Loaded multiplayer mod detected: " + selected + ".";
				}
				return "Loaded multiplayer mods detected; using " + selected + " by priority MorePlayersSimple > MorePlayers > RoboUnion.";
			}
		}

		public const string PluginGuid = "local.playercountfixed";

		public const string PluginName = "PlayerCountFixed";

		public const string PluginVersion = "1.0.0";

		private const string MorePlayersGuid = "ozoka.moreplayers";

		private const string MorePlayersSimpleGuid = "dyxc666.MorePlayersSimple";

		private const string RoboUnionGuid = "Linkoid.Repo.RoboUnion";

		private const string LobbyTextObjectName = "PlayerCountFixedTextLobby";

		private Harmony? _harmony;

		private MaxPlayerProvider? _maxPlayerProvider;

		private bool _loggedUiAttached;

		private bool _loggedUiAttachFailure;

		private bool _loggedUpdateFailure;

		private bool _loggedProviderFailure;

		internal static Plugin? Instance { get; private set; }

		private void Awake()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			Instance = this;
			_maxPlayerProvider = MaxPlayerProvider.Create(this);
			_harmony = new Harmony("local.playercountfixed");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PlayerCountFixed loaded.");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		internal static void TryAttachLobbyCounter(MenuPageLobby lobby)
		{
			Instance?.AttachLobbyCounter(lobby);
		}

		private void AttachLobbyCounter(MenuPageLobby lobby)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)lobby == (Object)null)
				{
					return;
				}
				Transform transform = ((Component)lobby).transform;
				Transform val = transform.Find("Panel");
				Transform val2 = val ?? transform;
				Transform val3 = FindExistingCounter(transform);
				if ((Object)(object)val3 != (Object)null)
				{
					if ((Object)(object)val != (Object)null && (Object)(object)val3.parent != (Object)(object)val)
					{
						val3.SetParent(val, false);
					}
					EnsureUpdater(((Component)val3).gameObject, ((Component)val3).GetComponent<TextMeshProUGUI>());
					return;
				}
				TextMeshProUGUI val4 = FindReferenceText(transform);
				GameObject val5 = new GameObject("PlayerCountFixedTextLobby");
				val5.transform.SetParent(val2, false);
				RectTransform obj = val5.AddComponent<RectTransform>();
				obj.anchorMin = new Vector2(0f, 1f);
				obj.anchorMax = new Vector2(0f, 1f);
				obj.pivot = new Vector2(0f, 1f);
				obj.anchoredPosition = new Vector2(295f, 30f);
				obj.sizeDelta = new Vector2(240f, 30f);
				TextMeshProUGUI val6 = val5.AddComponent<TextMeshProUGUI>();
				if ((Object)(object)val4 != (Object)null)
				{
					((TMP_Text)val6).font = ((TMP_Text)val4).font;
					((TMP_Text)val6).fontSharedMaterial = ((TMP_Text)val4).fontSharedMaterial;
					((TMP_Text)val6).fontStyle = ((TMP_Text)val4).fontStyle;
				}
				((TMP_Text)val6).alignment = (TextAlignmentOptions)513;
				((TMP_Text)val6).fontSize = 22f;
				((Graphic)val6).color = Color.yellow;
				((Graphic)val6).raycastTarget = false;
				((TMP_Text)val6).text = string.Empty;
				EnsureUpdater(val5, val6);
				if (!_loggedUiAttached)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Lobby player count UI attached.");
					_loggedUiAttached = true;
				}
			}
			catch (Exception ex)
			{
				LogUiAttachFailureOnce(ex);
			}
		}

		private static TextMeshProUGUI? FindReferenceText(Transform root)
		{
			Transform val = root.Find("Menu Button - Leave/ButtonText") ?? root.Find("Menu Button - Settings/ButtonText");
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponent<TextMeshProUGUI>();
		}

		private static Transform? FindExistingCounter(Transform root)
		{
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, "PlayerCountFixedTextLobby", StringComparison.Ordinal))
				{
					return val;
				}
			}
			return null;
		}

		private static void EnsureUpdater(GameObject go, TextMeshProUGUI? text)
		{
			if (!((Object)(object)go == (Object)null) && !((Object)(object)text == (Object)null))
			{
				(go.GetComponent<PlayerCountFixedUpdater>() ?? go.AddComponent<PlayerCountFixedUpdater>()).SetText(text);
			}
		}

		internal int ResolveMaxPlayers(Room room)
		{
			try
			{
				if (!PhotonNetwork.IsMasterClient)
				{
					return room.MaxPlayers;
				}
				int? num = _maxPlayerProvider?.TryGetMaxPlayers(room);
				if (num.HasValue && num.Value > 0)
				{
					return num.Value;
				}
			}
			catch (Exception ex)
			{
				LogProviderFailureOnce(ex);
			}
			return room.MaxPlayers;
		}

		internal void LogUpdateFailureOnce(Exception ex)
		{
			if (!_loggedUpdateFailure)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Player count update failed; suppressing further update errors. " + ex.GetType().Name + ": " + ex.Message));
				_loggedUpdateFailure = true;
			}
		}

		private void LogUiAttachFailureOnce(Exception ex)
		{
			if (!_loggedUiAttachFailure)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Lobby player count UI attach failed; suppressing further attach errors. " + ex.GetType().Name + ": " + ex.Message));
				_loggedUiAttachFailure = true;
			}
		}

		private void LogProviderFailureOnce(Exception ex)
		{
			if (!_loggedProviderFailure)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Configured max player lookup failed; falling back to Photon room max. " + ex.GetType().Name + ": " + ex.Message));
				_loggedProviderFailure = true;
			}
		}
	}
}