Decompiled source of MorePlayersPlus VC UI v3.0.0

MorePlayersPlus.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using FishySteamworks.Server;
using HarmonyLib;
using MelonLoader;
using Microsoft.CodeAnalysis;
using MorePlayersPlusStandalone;
using ReluProtocol.Enum;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "MorePlayersPlus_VC_UI", "3.0.0", "Arena+Kandru+NeoMimicry", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MorePlayersPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MorePlayersPlus")]
[assembly: AssemblyTitle("MorePlayersPlus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 MorePlayersPlusStandalone
{
	public class Mod : MelonMod
	{
		private sealed class OverlayBehaviour : MonoBehaviour
		{
			private void OnGUI()
			{
				DrawOverlay();
			}
		}

		private static class DiscordRpc
		{
			private static int _loggedUnavailable;

			public static void Update(int playerCount, bool loading)
			{
				try
				{
					for (int i = 0; i < 10; i++)
					{
						using NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(".", "discord-ipc-" + i, PipeDirection.InOut);
						try
						{
							namedPipeClientStream.Connect(100);
						}
						catch
						{
							goto end_IL_0024;
						}
						namedPipeClientStream.ReadTimeout = 1000;
						namedPipeClientStream.WriteTimeout = 1000;
						WriteFrame(namedPipeClientStream, 0, "{\"v\":1,\"client_id\":\"1551559380799856660\"}");
						ReadFrame(namedPipeClientStream);
						string text = (loading ? "Loading" : "In game");
						string payload = $"{{\"cmd\":\"SET_ACTIVITY\",\"args\":{{\"pid\":{Process.GetCurrentProcess().Id},\"activity\":{{\"details\":\"MIMESIS\",\"state\":\"{text} - {playerCount}/{32} players\",\"party\":{{\"id\":\"mimesis-moreplayersplus\",\"size\":[{playerCount},{32}],\"privacy\":1}},\"instance\":true}}}},\"nonce\":\"moreplayersplus\"}}";
						WriteFrame(namedPipeClientStream, 1, payload);
						return;
						end_IL_0024:;
					}
					if (Interlocked.Exchange(ref _loggedUnavailable, 1) == 0)
					{
						MelonLogger.Msg("[MorePlayersPlus] Discord Rich Presence is unavailable (Discord may not be running)");
					}
				}
				catch (Exception ex)
				{
					if (Interlocked.Exchange(ref _loggedUnavailable, 1) == 0)
					{
						MelonLogger.Warning("[MorePlayersPlus] Discord Rich Presence unavailable: " + ex.Message);
					}
				}
			}

			private static void WriteFrame(Stream stream, int opcode, string payload)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(payload);
				byte[] array = new byte[8];
				Buffer.BlockCopy(BitConverter.GetBytes(opcode), 0, array, 0, 4);
				Buffer.BlockCopy(BitConverter.GetBytes(bytes.Length), 0, array, 4, 4);
				stream.Write(array, 0, array.Length);
				stream.Write(bytes, 0, bytes.Length);
				stream.Flush();
			}

			private static void ReadFrame(Stream stream)
			{
				byte[] array = new byte[8];
				if (stream.Read(array, 0, array.Length) < array.Length)
				{
					return;
				}
				int num = BitConverter.ToInt32(array, 4);
				if (num <= 0 || num > 1048576)
				{
					return;
				}
				byte[] buffer = new byte[num];
				int num2;
				for (int i = 0; i < num; i += num2)
				{
					num2 = stream.Read(buffer, i, num - i);
					if (num2 <= 0)
					{
						break;
					}
				}
			}
		}

		[HarmonyPatch]
		private class DeathMatchLateJoinPatch
		{
			private static MethodBase TargetMethod()
			{
				return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp")?.GetType("DeathMatchRoom")?.GetMethod("CanEnterChannel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}

			private static bool Prefix(object __instance, long playerUID, ref MsgErrorCode __result)
			{
				return !TryAllowLateJoin(__instance, playerUID, out __result);
			}
		}

		[HarmonyPatch]
		private class LogisticsLateJoinPatch
		{
			private static MethodBase TargetMethod()
			{
				return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp")?.GetType("LogisticsRoom")?.GetMethod("CanEnterChannel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}

			private static bool Prefix(object __instance, long playerUID, ref MsgErrorCode __result)
			{
				return !TryAllowLateJoin(__instance, playerUID, out __result);
			}
		}

		[HarmonyPatch(typeof(ServerSocket), "GetMaximumClients")]
		private class GetMaxPatch
		{
			private static bool Prefix(ref int __result)
			{
				__result = 32;
				return false;
			}
		}

		[HarmonyPatch(typeof(ServerSocket), "SetMaximumClients")]
		private class SetMaxPatch
		{
			private static bool Prefix(ref int value)
			{
				value = 32;
				return true;
			}
		}

		[HarmonyPatch]
		private class DissonancePersist
		{
			private static MethodBase TargetMethod()
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Dissonance.DissonanceComms");
					if (!(type == null))
					{
						return AccessTools.Method(type, "OnEnable", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "Start", (Type[])null, (Type[])null);
					}
				}
				return null;
			}

			private static void Postfix(object __instance)
			{
				try
				{
					MonoBehaviour val = (MonoBehaviour)((__instance is MonoBehaviour) ? __instance : null);
					if (!((Object)(object)val == (Object)null))
					{
						Object.DontDestroyOnLoad((Object)(object)((Component)val).gameObject);
						Transform parent = ((Component)val).transform.parent;
						if ((Object)(object)parent != (Object)null)
						{
							Object.DontDestroyOnLoad((Object)(object)((Component)parent).gameObject);
						}
						MelonLogger.Msg("[MorePlayersPlus] Dissonance persisted for loading VC");
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Error(ex.Message);
				}
			}
		}

		[HarmonyPatch]
		private class DissonanceOnDisableBlock
		{
			private static MethodBase TargetMethod()
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Dissonance.DissonanceComms");
					if (type != null)
					{
						return AccessTools.Method(type, "OnDisable", (Type[])null, (Type[])null);
					}
				}
				return null;
			}

			private static bool Prefix()
			{
				if (IsLoadingScreenActive())
				{
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch]
		private class GameSettingsVoiceVolume
		{
			private static MethodBase TargetMethod()
			{
				return (AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp")?.GetType("UIPrefab_GameSettings"))?.GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}

			private static bool Prepare()
			{
				return TargetMethod() != null;
			}

			private static void Postfix(object __instance)
			{
				CreateVoiceVolumeUi(__instance);
				CreateFeatureToggleButtons(__instance);
			}
		}

		[HarmonyPatch]
		private class ResultArrayExpand
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				Assembly asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp");
				if (asm == null)
				{
					yield break;
				}
				string[] array = new string[3] { "UIPrefab_DeathMatchResult", "UIPrefab_SurvivalResult", "UIPrefab_CollectionResult" };
				foreach (string name in array)
				{
					MethodInfo methodInfo = asm.GetType(name)?.GetMethod("PatchParameter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (methodInfo?.GetMethodBody() != null)
					{
						yield return methodInfo;
					}
				}
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> ins)
			{
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Expected O, but got Unknown
				List<CodeInstruction> list = ins.ToList();
				for (int i = 0; i < list.Count - 1; i++)
				{
					if (list[i].opcode == OpCodes.Ldc_I4_4 && list[i + 1].opcode == OpCodes.Newarr)
					{
						Type type = list[i + 1].operand as Type;
						if (type != null && type.Name.Contains("DeathMatchPlayerResult"))
						{
							list[i] = new CodeInstruction(OpCodes.Ldc_I4, (object)32);
						}
					}
				}
				return list;
			}
		}

		[HarmonyPatch]
		private class InGameMenu_EnsureSlots
		{
			private static MethodBase TargetMethod()
			{
				Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp")?.GetTypes().FirstOrDefault((Type t) => t.Name == "UIPrefab_InGameMenu");
				if (type == null)
				{
					return null;
				}
				return AccessTools.Method(type, "Awake", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "Start", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "OnEnable", (Type[])null, (Type[])null);
			}

			private static void Postfix(object __instance)
			{
				try
				{
					FieldInfo field = __instance.GetType().GetField("playerUIElements", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || !(field.GetValue(__instance) is IList { Count: >=4, Count: <32 } list))
					{
						return;
					}
					object obj = list[1];
					Type type = obj.GetType();
					FieldInfo field2 = type.GetField("container");
					if (field2 == null)
					{
						return;
					}
					object? value = field2.GetValue(obj);
					GameObject val = (GameObject)((value is GameObject) ? value : null);
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					Transform parent = val.transform.parent;
					for (int i = list.Count; i < 32; i++)
					{
						GameObject val2 = Object.Instantiate<GameObject>(val, parent);
						((Object)val2).name = $"MorePlayersPlayer{i + 1}";
						val2.SetActive(false);
						object obj2 = Activator.CreateInstance(type);
						FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo in fields)
						{
							object value2 = fieldInfo.GetValue(obj);
							Component comp = (Component)((value2 is Component) ? value2 : null);
							if (comp != null)
							{
								Component val3 = ((IEnumerable<Component>)val2.GetComponentsInChildren(((object)comp).GetType(), true)).FirstOrDefault((Func<Component, bool>)((Component c) => ((Object)c.gameObject).name == ((Object)comp.gameObject).name));
								if ((Object)(object)val3 != (Object)null)
								{
									fieldInfo.SetValue(obj2, val3);
								}
							}
							else
							{
								fieldInfo.SetValue(obj2, value2);
							}
						}
						list.Add(obj2);
					}
					MelonLogger.Msg($"[MorePlayersPlus] InGameMenu extended to {32} slots");
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"InGameMenu patch: {arg}");
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnLoadingListButtonClicked;

			public static UnityAction <1>__OnSpeakingIndicatorButtonClicked;
		}

		public const int MAX_PLAYERS = 32;

		private const float DEFAULT_VOICE_VOLUME_MULTIPLIER = 1.5f;

		private static object _voiceVolumeEntry;

		private static object _showLoadingPlayerListEntry;

		private static object _showSpeakingIndicatorEntry;

		private static object _voiceVolumeLabel;

		private static object _masterVolumeLabel;

		private static object _micVolumeLabel;

		private static object _gameSettingsInstance;

		private static Transform _voiceSliderTransform;

		private static bool _masterVolumeBound;

		private static bool _micVolumeBound;

		private static bool _showLoadingPlayerList;

		private static bool _showSpeakingIndicator;

		private static float _voiceVolumeMultiplier = 1.5f;

		private static OverlayBehaviour _overlayBehaviour;

		private static GameObject _loadingListButton;

		private static GameObject _speakingIndicatorButton;

		private static GameObject _loadingListLabel;

		private static GameObject _speakingIndicatorLabel;

		private static object _languageSelectedLabel;

		private static bool _featureButtonsCreated;

		private const float FEATURE_ROW_SPACING = 52f;

		private static int _discordUpdatePending;

		private static float _nextDiscordUpdate;

		private static float _nextFeatureLabelRefresh;

		private const string DISCORD_APPLICATION_ID = "1551559380799856660";

		private float _nextVcCheck;

		public override void OnInitializeMelon()
		{
			InitializePreferences();
			EnsureOverlayBehaviour();
			((MelonBase)this).LoggerInstance.Msg($"MorePlayersPlus Standalone v3.0.0 Loaded! Max={32} - VC during loading + UI fix enabled");
			((MelonBase)this).LoggerInstance.Msg("This mod works with or without Kandru's mod. If Kandru's mod is present, this adds VC during loading.");
			((MelonBase)this).LoggerInstance.Msg($"VC volume multiplier: {_voiceVolumeMultiplier:0.0}x (1.0x = normal)");
			((MelonBase)this).LoggerInstance.Msg($"Loading player list: {_showLoadingPlayerList}; speaking indicator: {_showSpeakingIndicator}");
		}

		public override void OnUpdate()
		{
			if (!(Time.time < _nextVcCheck))
			{
				_nextVcCheck = Time.time + 1f;
				ApplyVoiceVolume();
				UpdateDiscordPresence();
				if (Time.time >= _nextFeatureLabelRefresh)
				{
					_nextFeatureLabelRefresh = Time.time + 0.5f;
					UpdateFeatureToggleLabels();
				}
				if (IsLoadingScreenActive())
				{
					UnmuteAllDissonance();
				}
			}
		}

		private static void EnsureOverlayBehaviour()
		{
			//IL_0014: 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)
			//IL_0029: Expected O, but got Unknown
			try
			{
				if (!((Object)(object)_overlayBehaviour != (Object)null))
				{
					GameObject val = new GameObject("MorePlayersPlus_Overlay");
					_overlayBehaviour = val.AddComponent<OverlayBehaviour>();
					Object.DontDestroyOnLoad((Object)val);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not create overlay: " + ex.Message);
			}
		}

		private static void GetVoicePlayers(List<string> players, List<string> speaking)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Dissonance.DissonanceComms");
					if (type == null)
					{
						continue;
					}
					Object[] array = Object.FindObjectsOfType(type);
					PropertyInfo property = type.GetProperty("Players", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					Object[] array2 = array;
					foreach (Object obj in array2)
					{
						if (!(property?.GetValue(obj) is IEnumerable enumerable))
						{
							continue;
						}
						foreach (object item in enumerable)
						{
							if (item != null)
							{
								Type type2 = item.GetType();
								string text = type2.GetProperty("Name")?.GetValue(item)?.ToString();
								if (string.IsNullOrWhiteSpace(text))
								{
									text = "Unknown player";
								}
								if (!players.Contains(text))
								{
									players.Add(text);
								}
								if (type2.GetProperty("IsSpeaking")?.GetValue(item) as bool? == true && !speaking.Contains(text))
								{
									speaking.Add(text);
								}
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not read VC player list: " + ex.Message);
			}
		}

		private static void DrawOverlay()
		{
			if (!_showLoadingPlayerList && !_showSpeakingIndicator)
			{
				return;
			}
			bool flag = IsLoadingScreenActive();
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			GetVoicePlayers(list, list2);
			List<string> list3 = new List<string>();
			if (_showLoadingPlayerList && flag)
			{
				list3.Add($"MorePlayersPlus  PLAYERS ({list.Count}/{32})");
				foreach (string item in list)
				{
					list3.Add("  " + item);
				}
			}
			if (_showSpeakingIndicator && list2.Count > 0)
			{
				list3.Add("Speaking: " + string.Join(", ", list2));
			}
			if (list3.Count == 0)
			{
				return;
			}
			try
			{
				Type type = FindLoadedType("UnityEngine.GUI");
				Type rectType = FindLoadedType("UnityEngine.Rect");
				ConstructorInfo constructorInfo = rectType?.GetConstructor(new Type[4]
				{
					typeof(float),
					typeof(float),
					typeof(float),
					typeof(float)
				});
				MethodInfo methodInfo = type?.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "Label" && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == rectType && m.GetParameters()[1].ParameterType == typeof(string));
				if (constructorInfo == null || methodInfo == null)
				{
					return;
				}
				float num = 24f;
				foreach (string item2 in list3)
				{
					object obj = constructorInfo.Invoke(new object[4] { 24f, num, 900f, 26f });
					methodInfo.Invoke(null, new object[2] { obj, item2 });
					num += 24f;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not draw overlay: " + ex.Message);
			}
		}

		private static void InitializePreferences()
		{
			try
			{
				Type type = typeof(MelonMod).Assembly.GetType("MelonLoader.MelonPreferences");
				if (type == null)
				{
					return;
				}
				MethodInfo methodInfo = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public)
					where m.Name == "CreateCategory"
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
				if (methodInfo == null)
				{
					return;
				}
				object[] parameters = ((methodInfo.GetParameters().Length != 1) ? new object[2] { "MorePlayersPlus", "MorePlayersPlus" } : new object[1] { "MorePlayersPlus" });
				object obj = methodInfo.Invoke(null, parameters);
				if (obj == null)
				{
					return;
				}
				MethodInfo methodInfo2 = (from m in obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public)
					where m.Name == "CreateEntry" && m.IsGenericMethodDefinition
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
				if (methodInfo2 == null)
				{
					return;
				}
				MethodInfo methodInfo3 = methodInfo2.MakeGenericMethod(typeof(float));
				ParameterInfo[] parameters2 = methodInfo3.GetParameters();
				object[] array = new object[parameters2.Length];
				for (int num = 0; num < parameters2.Length; num++)
				{
					switch (num)
					{
					case 0:
						array[num] = "VoiceVolumeMultiplier";
						continue;
					case 1:
						array[num] = 1.5f;
						continue;
					}
					if (parameters2[num].ParameterType == typeof(string))
					{
						array[num] = "VC volume multiplier (1.0 = normal)";
					}
					else if (parameters2[num].ParameterType == typeof(bool))
					{
						array[num] = false;
					}
					else
					{
						array[num] = null;
					}
				}
				_voiceVolumeEntry = methodInfo3.Invoke(obj, array);
				if ((_voiceVolumeEntry?.GetType().GetProperty("Value") ?? _voiceVolumeEntry?.GetType().GetProperty("EditedValue"))?.GetValue(_voiceVolumeEntry) is float val)
				{
					_voiceVolumeMultiplier = Math.Max(0f, Math.Min(4f, val));
				}
				MethodInfo methodInfo4 = methodInfo2.MakeGenericMethod(typeof(bool));
				ParameterInfo[] parameters3 = methodInfo4.GetParameters();
				object[] array2 = new object[parameters3.Length];
				for (int num2 = 0; num2 < parameters3.Length; num2++)
				{
					switch (num2)
					{
					case 0:
						array2[num2] = "ShowLoadingPlayerList";
						continue;
					case 1:
						array2[num2] = false;
						continue;
					}
					if (parameters3[num2].ParameterType == typeof(string))
					{
						array2[num2] = "Show player list during loading screens";
					}
					else if (parameters3[num2].ParameterType == typeof(bool))
					{
						array2[num2] = false;
					}
					else
					{
						array2[num2] = null;
					}
				}
				_showLoadingPlayerListEntry = methodInfo4.Invoke(obj, array2);
				object[] array3 = new object[parameters3.Length];
				for (int num3 = 0; num3 < parameters3.Length; num3++)
				{
					switch (num3)
					{
					case 0:
						array3[num3] = "ShowSpeakingIndicator";
						continue;
					case 1:
						array3[num3] = false;
						continue;
					}
					if (parameters3[num3].ParameterType == typeof(string))
					{
						array3[num3] = "Show who is speaking";
					}
					else if (parameters3[num3].ParameterType == typeof(bool))
					{
						array3[num3] = false;
					}
					else
					{
						array3[num3] = null;
					}
				}
				_showSpeakingIndicatorEntry = methodInfo4.Invoke(obj, array3);
				object obj2 = _showLoadingPlayerListEntry?.GetType().GetProperty("Value")?.GetValue(_showLoadingPlayerListEntry);
				object obj3 = _showSpeakingIndicatorEntry?.GetType().GetProperty("Value")?.GetValue(_showSpeakingIndicatorEntry);
				if (obj2 is bool showLoadingPlayerList)
				{
					_showLoadingPlayerList = showLoadingPlayerList;
				}
				if (obj3 is bool showSpeakingIndicator)
				{
					_showSpeakingIndicator = showSpeakingIndicator;
				}
				type.GetMethod("Save", Type.EmptyTypes)?.Invoke(null, null);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not initialize VC volume setting: " + ex.Message);
			}
		}

		private static void ApplyVoiceVolume()
		{
			if (_voiceVolumeMultiplier <= 0f)
			{
				return;
			}
			try
			{
				Type type = (from a in AppDomain.CurrentDomain.GetAssemblies()
					select a.GetType("Dissonance.Audio.Playback.VoicePlayback")).FirstOrDefault((Type t) => t != null);
				if (type == null)
				{
					return;
				}
				PropertyInfo property = type.GetProperty("AudioSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Object[] array = Object.FindObjectsOfType(type);
				foreach (Object val in array)
				{
					if (val == (Object)null)
					{
						continue;
					}
					object obj = property?.GetValue(val);
					if (obj != null)
					{
						PropertyInfo property2 = obj.GetType().GetProperty("volume", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if ((object)property2 != null && property2.CanWrite)
						{
							property2.SetValue(obj, _voiceVolumeMultiplier);
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not apply VC volume: " + ex.Message);
			}
		}

		private static void SetVoiceVolumeMultiplier(float value)
		{
			_voiceVolumeMultiplier = Math.Max(0f, Math.Min(4f, value));
			try
			{
				PropertyInfo propertyInfo = _voiceVolumeEntry?.GetType().GetProperty("Value") ?? _voiceVolumeEntry?.GetType().GetProperty("EditedValue");
				if ((object)propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(_voiceVolumeEntry, _voiceVolumeMultiplier);
				}
				typeof(MelonMod).Assembly.GetType("MelonLoader.MelonPreferences")?.GetMethod("Save", Type.EmptyTypes)?.Invoke(null, null);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not save VC volume setting: " + ex.Message);
			}
			UpdateVoiceVolumeLabel();
			ApplyVoiceVolume();
		}

		private static void OnVoiceVolumeSliderChanged(float value)
		{
			SetVoiceVolumeMultiplier(value);
		}

		private static void UpdateVoiceVolumeLabel()
		{
			if (_voiceVolumeLabel != null)
			{
				SetLabelText(_voiceVolumeLabel, $"VC VOLUME  {Math.Round(_voiceVolumeMultiplier * 100f):0}%");
			}
		}

		private static void SetPreferenceValue(object entry, bool value)
		{
			try
			{
				PropertyInfo propertyInfo = entry?.GetType().GetProperty("Value") ?? entry?.GetType().GetProperty("EditedValue");
				if ((object)propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(entry, value);
				}
				typeof(MelonMod).Assembly.GetType("MelonLoader.MelonPreferences")?.GetMethod("Save", Type.EmptyTypes)?.Invoke(null, null);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not save feature setting: " + ex.Message);
			}
		}

		private static void UpdateFeatureToggleLabels()
		{
			bool flag = IsJapaneseUi();
			SetButtonText(_loadingListButton, flag ? "オフ" : "OFF");
			SetButtonText(_speakingIndicatorButton, flag ? "オン" : "ON");
			SetButtonText(_loadingListLabel, flag ? "ロード中プレイヤー表示" : "LOADING PLAYERS");
			SetButtonText(_speakingIndicatorLabel, flag ? "発話インジケータ" : "SPEAKING INDICATOR");
			SetButtonText(_loadingListButton, (!flag) ? (_showLoadingPlayerList ? "ON" : "OFF") : (_showLoadingPlayerList ? "オン" : "オフ"));
			SetButtonText(_speakingIndicatorButton, (!flag) ? (_showSpeakingIndicator ? "ON" : "OFF") : (_showSpeakingIndicator ? "オン" : "オフ"));
		}

		private static bool IsJapaneseUi()
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Invalid comparison between Unknown and I4
			try
			{
				string text = _languageSelectedLabel?.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(_languageSelectedLabel)?.ToString();
				if (!string.IsNullOrWhiteSpace(text))
				{
					if (text.Contains("日本") || text.Contains("Japanese") || text.Equals("ja", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text.Contains("English") || text.Equals("en", StringComparison.OrdinalIgnoreCase))
					{
						return false;
					}
				}
				string[] array = new string[4] { "Language", "language", "CurrentLanguage", "SelectedLanguage" };
				foreach (string text2 in array)
				{
					if (PlayerPrefs.HasKey(text2))
					{
						string text3 = PlayerPrefs.GetString(text2, string.Empty);
						if (text3.Contains("日本") || text3.Contains("Japanese") || text3.Equals("ja", StringComparison.OrdinalIgnoreCase))
						{
							return true;
						}
						if (text3.Contains("English") || text3.Equals("en", StringComparison.OrdinalIgnoreCase))
						{
							return false;
						}
					}
				}
			}
			catch
			{
			}
			return (int)Application.systemLanguage == 22;
		}

		private static void SetButtonText(GameObject buttonObject, string text)
		{
			if ((Object)(object)buttonObject == (Object)null)
			{
				return;
			}
			Type type = FindLoadedType("TMPro.TMP_Text");
			if (!(type == null))
			{
				Component[] componentsInChildren = buttonObject.GetComponentsInChildren(type, true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					SetLabelText(componentsInChildren[i], text);
				}
			}
		}

		private static void OnLoadingListButtonClicked()
		{
			_showLoadingPlayerList = !_showLoadingPlayerList;
			SetPreferenceValue(_showLoadingPlayerListEntry, _showLoadingPlayerList);
			UpdateFeatureToggleLabels();
		}

		private static void OnSpeakingIndicatorButtonClicked()
		{
			_showSpeakingIndicator = !_showSpeakingIndicator;
			SetPreferenceValue(_showSpeakingIndicatorEntry, _showSpeakingIndicator);
			UpdateFeatureToggleLabels();
		}

		private static void OnFeatureButtonMouseOver()
		{
		}

		private static void OnFeatureButtonMouseExit()
		{
		}

		private static void ConfigureFeatureButtonHighlight(object settingsInstance, GameObject buttonObject)
		{
			try
			{
				Type type = settingsInstance.GetType();
				Type type2 = FindLoadedType("TMPro.TMP_Text");
				if (!(type2 == null))
				{
					Component val = buttonObject.GetComponentsInChildren(type2, true).FirstOrDefault();
					MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddMouseOverEnterEvent" && m.GetParameters().Length == 3);
					MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddMouseOverExitEvent" && m.GetParameters().Length == 3);
					if (!((Object)(object)val == (Object)null) && !(methodInfo == null) && !(methodInfo2 == null))
					{
						Type parameterType = methodInfo.GetParameters()[2].ParameterType;
						MethodInfo method = typeof(Mod).GetMethod("OnFeatureButtonMouseOver", BindingFlags.Static | BindingFlags.NonPublic);
						MethodInfo method2 = typeof(Mod).GetMethod("OnFeatureButtonMouseExit", BindingFlags.Static | BindingFlags.NonPublic);
						Delegate obj = Delegate.CreateDelegate(parameterType, method);
						Delegate obj2 = Delegate.CreateDelegate(parameterType, method2);
						methodInfo.Invoke(settingsInstance, new object[3] { buttonObject, val, obj });
						methodInfo2.Invoke(settingsInstance, new object[3] { buttonObject, val, obj2 });
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not register feature-button highlight: " + ex.Message);
			}
		}

		private static void CreateFeatureToggleButtons(object settingsInstance)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			if (settingsInstance == null || (_featureButtonsCreated && (Object)(object)_loadingListButton != (Object)null && (Object)(object)_speakingIndicatorButton != (Object)null))
			{
				return;
			}
			try
			{
				Type type = settingsInstance.GetType();
				_languageSelectedLabel = type.GetField("_UE_Dropdown_selectLanguage_Label", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(settingsInstance);
				object obj = type.GetField("_UE_LANGUAGE", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(settingsInstance);
				object languageSelectedLabel = _languageSelectedLabel;
				if (obj != null && languageSelectedLabel != null)
				{
					_loadingListLabel = CreateFeatureLabelElement(obj, "MorePlayersPlus_LoadingPlayerListLabel", -0f);
					_speakingIndicatorLabel = CreateFeatureLabelElement(obj, "MorePlayersPlus_SpeakingIndicatorLabel", -52f);
					object obj2 = <>O.<0>__OnLoadingListButtonClicked;
					if (obj2 == null)
					{
						UnityAction val = OnLoadingListButtonClicked;
						<>O.<0>__OnLoadingListButtonClicked = val;
						obj2 = (object)val;
					}
					_loadingListButton = CreateFeatureValueElement(languageSelectedLabel, "MorePlayersPlus_LoadingPlayerListValue", -0f, (UnityAction)obj2);
					object obj3 = <>O.<1>__OnSpeakingIndicatorButtonClicked;
					if (obj3 == null)
					{
						UnityAction val2 = OnSpeakingIndicatorButtonClicked;
						<>O.<1>__OnSpeakingIndicatorButtonClicked = val2;
						obj3 = (object)val2;
					}
					_speakingIndicatorButton = CreateFeatureValueElement(languageSelectedLabel, "MorePlayersPlus_SpeakingIndicatorValue", -52f, (UnityAction)obj3);
					AlignValueToLabel(_loadingListButton, _loadingListLabel);
					AlignValueToLabel(_speakingIndicatorButton, _speakingIndicatorLabel);
					ConfigureFeatureButtonHighlight(settingsInstance, _loadingListButton);
					ConfigureFeatureButtonHighlight(settingsInstance, _speakingIndicatorButton);
					float offset = -104f;
					MoveSettingRow(type, settingsInstance, "_UE_Dropdown_selectLanguage", offset);
					MoveSettingRow(type, settingsInstance, "_UE_Dropdown_selectLanguage_Label", offset);
					MoveSettingRow(type, settingsInstance, "_UE_LANGUAGE", offset);
					MoveSettingRow(type, settingsInstance, "_UE_CreatorCode", offset);
					MoveSettingRow(type, settingsInstance, "_UE_CreateCodeText", offset);
					MoveSettingRow(type, settingsInstance, "_UE_lastInputedCreatorCode", offset);
					_featureButtonsCreated = true;
					UpdateFeatureToggleLabels();
					MelonLogger.Msg("[MorePlayersPlus] Added display-style player-list and speaking-indicator rows to Game Settings");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not add feature toggles: " + ex.Message);
			}
		}

		private static GameObject CreateFeatureLabelElement(object template, string name, float offsetY)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			object? obj = template?.GetType().GetProperty("gameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(template);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			Transform val2 = ((val != null) ? val.transform : null);
			if ((Object)(object)val == (Object)null || (Object)(object)((val2 != null) ? val2.parent : null) == (Object)null)
			{
				return null;
			}
			GameObject obj2 = Object.Instantiate<GameObject>(val, val2.parent);
			((Object)obj2).name = name;
			obj2.transform.localPosition = val2.localPosition + new Vector3(0f, offsetY, 0f);
			obj2.transform.localScale = val2.localScale;
			return obj2;
		}

		private static GameObject CreateFeatureValueElement(object template, string name, float offsetY, UnityAction onClick)
		{
			GameObject val = CreateFeatureLabelElement(template, name, offsetY);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Button val2 = val.GetComponent<Button>() ?? val.AddComponent<Button>();
			((Selectable)val2).transition = (Transition)0;
			((UnityEventBase)val2.onClick).RemoveAllListeners();
			((UnityEvent)val2.onClick).AddListener(onClick);
			Type type = FindLoadedType("TMPro.TMP_Text");
			Graphic val3 = (Graphic)((type == null) ? null : /*isinst with value type is only supported in some contexts*/);
			if ((Object)(object)val3 != (Object)null)
			{
				((Selectable)val2).targetGraphic = val3;
			}
			return val;
		}

		private static void AlignValueToLabel(GameObject valueObject, GameObject labelObject)
		{
			//IL_0019: 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_0027: 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)
			if (!((Object)(object)valueObject == (Object)null) && !((Object)(object)labelObject == (Object)null))
			{
				Vector3 position = valueObject.transform.position;
				position.y = labelObject.transform.position.y;
				valueObject.transform.position = position;
			}
		}

		private static void MoveSettingRow(Type settingsType, object settingsInstance, string fieldName, float offset)
		{
			object obj = settingsType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(settingsInstance);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			if ((Object)(object)val != (Object)null)
			{
				SetLocalPositionY(val, offset);
				return;
			}
			object? obj2 = obj?.GetType().GetProperty("gameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
			GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null);
			if ((Object)(object)val2 != (Object)null)
			{
				SetLocalPositionY(val2.transform, offset);
			}
		}

		private static void UpdateDiscordPresence()
		{
			if (Time.time < _nextDiscordUpdate)
			{
				return;
			}
			_nextDiscordUpdate = Time.time + 15f;
			if (Interlocked.Exchange(ref _discordUpdatePending, 1) != 0)
			{
				return;
			}
			List<string> list = new List<string>();
			List<string> speaking = new List<string>();
			GetVoicePlayers(list, speaking);
			int playerCount = Math.Max(1, Math.Min(32, list.Count + 1));
			bool loading = IsLoadingScreenActive();
			ThreadPool.QueueUserWorkItem(delegate
			{
				try
				{
					DiscordRpc.Update(playerCount, loading);
				}
				finally
				{
					Interlocked.Exchange(ref _discordUpdatePending, 0);
				}
			});
		}

		private static void SetLabelText(object label, string text)
		{
			(label?.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.SetValue(label, text);
		}

		private static float GetSliderPercent(object slider, Type sliderType)
		{
			float num = Convert.ToSingle(sliderType.GetProperty("value")?.GetValue(slider) ?? ((object)0f));
			float num2 = Convert.ToSingle(sliderType.GetProperty("minValue")?.GetValue(slider) ?? ((object)0f));
			float num3 = Convert.ToSingle(sliderType.GetProperty("maxValue")?.GetValue(slider) ?? ((object)1f));
			if (num3 <= num2)
			{
				return 0f;
			}
			return Math.Max(0f, Math.Min(100f, (num - num2) / (num3 - num2) * 100f));
		}

		private static void UpdateMasterVolumeLabel(float value)
		{
			SetLabelText(_masterVolumeLabel, $"MASTER VOLUME  {Math.Round(value):0}%");
		}

		private static void UpdateMicVolumeLabel(float value)
		{
			SetLabelText(_micVolumeLabel, $"MIC VOLUME  {Math.Round(value):0}%");
		}

		private static void ConfigureExistingVolumeLabel(object settingsInstance, string sliderFieldName, string labelFieldName, bool master)
		{
			if (master ? _masterVolumeBound : _micVolumeBound)
			{
				return;
			}
			Type type = settingsInstance.GetType();
			object? obj = type.GetField(sliderFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(settingsInstance);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			object obj2 = type.GetField(labelFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(settingsInstance);
			Type type2 = FindLoadedType("UnityEngine.UI.Slider");
			if ((Object)(object)val == (Object)null || obj2 == null || type2 == null)
			{
				return;
			}
			Component val2 = ((Component)val).gameObject.GetComponentsInChildren(type2, true).FirstOrDefault();
			if (!((Object)(object)val2 == (Object)null))
			{
				if (master)
				{
					_masterVolumeLabel = obj2;
				}
				else
				{
					_micVolumeLabel = obj2;
				}
				object obj3 = type2.GetProperty("onValueChanged")?.GetValue(val2);
				MethodInfo methodInfo = obj3?.GetType().GetMethod("AddListener");
				if (methodInfo != null)
				{
					Type parameterType = methodInfo.GetParameters()[0].ParameterType;
					string name = (master ? "UpdateMasterVolumeLabel" : "UpdateMicVolumeLabel");
					MethodInfo method = typeof(Mod).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic);
					Delegate obj4 = Delegate.CreateDelegate(parameterType, method);
					methodInfo.Invoke(obj3, new object[1] { obj4 });
				}
				float sliderPercent = GetSliderPercent(val2, type2);
				if (master)
				{
					_masterVolumeBound = true;
					UpdateMasterVolumeLabel(sliderPercent);
				}
				else
				{
					_micVolumeBound = true;
					UpdateMicVolumeLabel(sliderPercent);
				}
			}
		}

		private static void SetLocalPositionY(Transform transform, float offset)
		{
			try
			{
				PropertyInfo property = ((object)transform).GetType().GetProperty("localPosition");
				object obj = property?.GetValue(transform);
				FieldInfo fieldInfo = obj?.GetType().GetField("y");
				if (!(property == null) && obj != null && !(fieldInfo == null))
				{
					float num = (float)fieldInfo.GetValue(obj);
					fieldInfo.SetValue(obj, num + offset);
					property.SetValue(transform, obj);
				}
			}
			catch
			{
			}
		}

		private static void SetVoiceSliderHighlight(bool highlighted)
		{
			try
			{
				(_gameSettingsInstance?.GetType().GetMethod("SliderMouseOverImage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(_gameSettingsInstance, new object[3] { _voiceSliderTransform, highlighted, false });
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not update VC slider highlight: " + ex.Message);
			}
		}

		private static void OnVoiceSliderMouseOver()
		{
			SetVoiceSliderHighlight(highlighted: true);
		}

		private static void OnVoiceSliderMouseExit()
		{
			SetVoiceSliderHighlight(highlighted: false);
		}

		private static void ConfigureVoiceSliderHighlight(object settingsInstance, GameObject sliderClone, GameObject labelClone)
		{
			try
			{
				Type type = settingsInstance.GetType();
				MethodInfo method = type.GetMethod("AddMouseOverEnterEvent", BindingFlags.Instance | BindingFlags.NonPublic);
				MethodInfo method2 = type.GetMethod("AddMouseOverExitEvent", BindingFlags.Instance | BindingFlags.NonPublic);
				Type type2 = FindLoadedType("UnityEngine.UI.Image");
				Type type3 = FindLoadedType("TMPro.TMP_Text");
				if (method == null || method2 == null || type2 == null || type3 == null)
				{
					return;
				}
				object obj = Activator.CreateInstance(typeof(List<>).MakeGenericType(type2));
				object obj2 = Activator.CreateInstance(typeof(List<>).MakeGenericType(type3));
				Component[] componentsInChildren = sliderClone.GetComponentsInChildren(type2, true);
				foreach (Component value in componentsInChildren)
				{
					((IList)obj).Add(value);
				}
				if ((Object)(object)labelClone != (Object)null)
				{
					componentsInChildren = labelClone.GetComponentsInChildren(type3, true);
					foreach (Component value2 in componentsInChildren)
					{
						((IList)obj2).Add(value2);
					}
				}
				Type parameterType = method.GetParameters()[3].ParameterType;
				MethodInfo method3 = typeof(Mod).GetMethod("OnVoiceSliderMouseOver", BindingFlags.Static | BindingFlags.NonPublic);
				MethodInfo method4 = typeof(Mod).GetMethod("OnVoiceSliderMouseExit", BindingFlags.Static | BindingFlags.NonPublic);
				Delegate obj3 = Delegate.CreateDelegate(parameterType, method3);
				Delegate obj4 = Delegate.CreateDelegate(parameterType, method4);
				_gameSettingsInstance = settingsInstance;
				_voiceSliderTransform = sliderClone.transform;
				method.Invoke(settingsInstance, new object[4] { sliderClone, obj, obj2, obj3 });
				method2.Invoke(settingsInstance, new object[4] { sliderClone, obj, obj2, obj4 });
				SetVoiceSliderHighlight(highlighted: false);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not register VC slider highlight: " + ex.Message);
			}
		}

		private static Type FindLoadedType(string fullName)
		{
			return (from a in AppDomain.CurrentDomain.GetAssemblies()
				select a.GetType(fullName)).FirstOrDefault((Type t) => t != null);
		}

		private static void CreateVoiceVolumeUi(object settingsInstance)
		{
			if (settingsInstance == null)
			{
				return;
			}
			try
			{
				Type type = settingsInstance.GetType();
				ConfigureExistingVolumeLabel(settingsInstance, "_UE_MasterVolume_Slider", "_UE_MASTER_VOLUME", master: true);
				ConfigureExistingVolumeLabel(settingsInstance, "_UE_MicVolume_Slider", "_UE_Mic_VOLUME", master: false);
				if (_voiceVolumeLabel != null)
				{
					return;
				}
				FieldInfo field = type.GetField("_UE_MicVolume_Slider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo? field2 = type.GetField("_UE_Mic_VOLUME", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				object? obj = field?.GetValue(settingsInstance);
				Transform val = (Transform)((obj is Transform) ? obj : null);
				object obj2 = field2?.GetValue(settingsInstance);
				if ((Object)(object)val == (Object)null || obj2 == null)
				{
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, val.parent);
				((Object)val2).name = "MorePlayersPlus_VoiceVolumeSlider";
				SetLocalPositionY(val2.transform, -52f);
				Type type2 = FindLoadedType("UnityEngine.UI.Slider");
				Component val3 = ((type2 == null) ? null : val2.GetComponentsInChildren(type2, true).FirstOrDefault());
				if (!((Object)(object)val3 == (Object)null))
				{
					type2.GetProperty("minValue")?.SetValue(val3, 0f);
					type2.GetProperty("maxValue")?.SetValue(val3, 4f);
					object obj3 = type2.GetProperty("onValueChanged")?.GetValue(val3);
					obj3?.GetType().GetMethod("RemoveAllListeners")?.Invoke(obj3, null);
					MethodInfo methodInfo = obj3?.GetType().GetMethod("AddListener");
					if (methodInfo != null)
					{
						Type parameterType = methodInfo.GetParameters()[0].ParameterType;
						MethodInfo method = typeof(Mod).GetMethod("OnVoiceVolumeSliderChanged", BindingFlags.Static | BindingFlags.NonPublic);
						Delegate obj4 = Delegate.CreateDelegate(parameterType, method);
						methodInfo.Invoke(obj3, new object[1] { obj4 });
					}
					MethodInfo method2 = type2.GetMethod("SetValueWithoutNotify");
					if (method2 != null)
					{
						method2.Invoke(val3, new object[1] { _voiceVolumeMultiplier });
					}
					else
					{
						type2.GetProperty("value")?.SetValue(val3, _voiceVolumeMultiplier);
					}
					object? obj5 = obj2.GetType().GetProperty("gameObject")?.GetValue(obj2);
					GameObject val4 = (GameObject)((obj5 is GameObject) ? obj5 : null);
					Transform val5 = ((val4 != null) ? val4.transform : null);
					GameObject val6 = null;
					if ((Object)(object)val4 != (Object)null && (Object)(object)((val5 != null) ? val5.parent : null) != (Object)null)
					{
						val6 = Object.Instantiate<GameObject>(val4, val5.parent);
						((Object)val6).name = "MorePlayersPlus_VoiceVolumeLabel";
						SetLocalPositionY(val6.transform, -52f);
						Type type3 = FindLoadedType("TMPro.TMP_Text");
						_voiceVolumeLabel = ((type3 == null) ? null : val6.GetComponentsInChildren(type3, true).FirstOrDefault());
						UpdateVoiceVolumeLabel();
					}
					ConfigureVoiceSliderHighlight(settingsInstance, val2, val6);
					MelonLogger.Msg("[MorePlayersPlus] Added VC volume control to Game Settings");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Could not add VC volume control UI: " + ex.Message);
			}
		}

		private static bool IsLoadingScreenActive()
		{
			//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)
			try
			{
				MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>();
				foreach (MonoBehaviour val in array)
				{
					if (!((Object)(object)val == (Object)null))
					{
						string name = ((object)val).GetType().Name;
						if ((name.Contains("Loading") || name.Contains("Transition")) && ((Component)val).gameObject.activeInHierarchy)
						{
							return true;
						}
					}
				}
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).name.Contains("Loading"))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void UnmuteAllDissonance()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Dissonance.DissonanceComms");
					if (type == null)
					{
						continue;
					}
					Object[] array = Object.FindObjectsOfType(type);
					foreach (Object obj in array)
					{
						PropertyInfo propertyInfo = AccessTools.Property(type, "IsMuted");
						if (propertyInfo != null && (bool)propertyInfo.GetValue(obj))
						{
							propertyInfo.SetValue(obj, false);
						}
						PropertyInfo propertyInfo2 = AccessTools.Property(type, "IsMicMuted");
						if (propertyInfo2 != null && (bool)propertyInfo2.GetValue(obj))
						{
							propertyInfo2.SetValue(obj, false);
						}
					}
				}
			}
			catch
			{
			}
		}

		private static bool TryAllowLateJoin(object room, long playerUID, out MsgErrorCode result)
		{
			result = (MsgErrorCode)0;
			try
			{
				Type type = room.GetType();
				object obj = (type.BaseType?.GetProperty("IsSessionEnding", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(room);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return false;
				}
				object obj2 = type.GetField("_state", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(room);
				if (obj2 == null)
				{
					return false;
				}
				string text = obj2.ToString();
				bool flag2 = type.Name == "DeathMatchRoom";
				bool flag3 = type.Name == "LogisticsRoom";
				bool num2;
				if (!flag2)
				{
					if (!flag3 || !(text != "Invalid") || !(text != "Ready"))
					{
						goto IL_00f3;
					}
					num2 = text != "GameOver";
				}
				else
				{
					if (text == "OnPlaying")
					{
						goto IL_00fb;
					}
					num2 = text == "LastManStanding";
				}
				if (!num2)
				{
					goto IL_00f3;
				}
				goto IL_00fb;
				IL_00f3:
				return false;
				IL_00fb:
				if ((type.BaseType?.GetMethod("FindPlayerByUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(room, new object[1] { playerUID }) != null)
				{
					result = (MsgErrorCode)12;
					return true;
				}
				object obj3 = (type.BaseType?.GetField("_vPlayerDict", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(room);
				if (Convert.ToInt32(obj3?.GetType().GetProperty("Count")?.GetValue(obj3) ?? ((object)0)) >= 32)
				{
					result = (MsgErrorCode)18;
					return true;
				}
				result = (MsgErrorCode)0;
				return true;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[MorePlayersPlus] Late-join check failed: " + ex.Message);
				return false;
			}
		}
	}
}