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 d__7 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PlayerCountFixedUpdater <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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(d__7))] private IEnumerator RefreshLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new 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 entry = default(ConfigEntry); if (!val.TryGetEntry(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()); return; } TextMeshProUGUI val4 = FindReferenceText(transform); GameObject val5 = new GameObject("PlayerCountFixedTextLobby"); val5.transform.SetParent(val2, false); RectTransform obj = val5.AddComponent(); 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(); 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(); } private static Transform? FindExistingCounter(Transform root) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(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() ?? go.AddComponent()).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; } } } }