Decompiled source of FusionPersonalBlock v1.0.1

Mods\FusionPersonalBlock.dll

Decompiled 11 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using BoneLib.BoneMenu;
using BoneLib.Notifications;
using FusionPersonalBlock;
using Il2CppSLZ.Marrow;
using LabFusion.Entities;
using LabFusion.Extensions;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.Utilities;
using LabFusion.Voice;
using MelonLoader;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "Fusion Personal Block", "1.0.1", "seeleyllp-crypto", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FusionPersonalBlock
{
	public sealed class Core : MelonMod
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ServerEvent <0>__OnFusionJoinedServer;

			public static ServerEvent <1>__OnFusionDisconnected;

			public static PlayerUpdate <2>__OnFusionPlayerJoined;

			public static PlayerUpdate <3>__OnFusionPlayerLeft;

			public static Action<NetworkPlayer> <4>__OnNetworkPlayerRegistered;

			public static Action <5>__ShowSelectedPlayer;

			public static Action <6>__RefreshPlayerPages;

			public static Action <7>__BlockSelectedPlayer;

			public static Action <8>__UnblockSelectedPlayer;

			public static Action<bool> <9>__SetMuteBlockedPlayers;

			public static Action<bool> <10>__SetHideBlockedPlayers;

			public static Action<bool> <11>__SetDisableRigGrabbing;

			public static Action<bool> <12>__SetAutoLeaveBlockedPlayer;

			public static Action <13>__ApplyEffectsNow;

			public static Action <14>__LeaveCurrentLobby;

			public static Action <15>__ShowBlockStatus;

			public static Action <16>__OpenDataFolder;
		}

		private const string ModVersion = "1.0.1";

		private const int MaximumSavedUsers = 500;

		private static readonly ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();

		private static readonly Dictionary<ulong, bool> OriginalForceHide = new Dictionary<ulong, bool>();

		private static readonly Dictionary<ulong, float> OriginalVoiceVolume = new Dictionary<ulong, float>();

		private static readonly Dictionary<ulong, bool> OriginalVoiceMuted = new Dictionary<ulong, bool>();

		private static readonly Dictionary<ulong, List<GripInteractionState>> DisabledGripStates = new Dictionary<ulong, List<GripInteractionState>>();

		private static PersonalBlockSettings _settings = new PersonalBlockSettings();

		private static string _dataDirectory = string.Empty;

		private static string _settingsPath = string.Empty;

		private static Page? _lobbyPlayersPage;

		private static Page? _blockedPlayersPage;

		private static ulong? _selectedSteamId;

		private static string _selectedName = string.Empty;

		private static string _pendingAction = string.Empty;

		private static ulong? _pendingActionSteamId;

		private static DateTimeOffset _pendingActionExpiresUtc;

		private static float _effectTimer;

		private static bool _autoLeaveQueued;

		public override void OnInitializeMelon()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00ad: 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_00b8: Expected O, but got Unknown
			_dataDirectory = Path.Combine(MelonEnvironment.UserDataDirectory, "FusionPersonalBlock");
			_settingsPath = Path.Combine(_dataDirectory, "blocked-users.json");
			Directory.CreateDirectory(_dataDirectory);
			LoadSettings();
			BuildBoneMenu();
			object obj = <>O.<0>__OnFusionJoinedServer;
			if (obj == null)
			{
				ServerEvent val = OnFusionJoinedServer;
				<>O.<0>__OnFusionJoinedServer = val;
				obj = (object)val;
			}
			MultiplayerHooking.OnJoinedServer += (ServerEvent)obj;
			object obj2 = <>O.<1>__OnFusionDisconnected;
			if (obj2 == null)
			{
				ServerEvent val2 = OnFusionDisconnected;
				<>O.<1>__OnFusionDisconnected = val2;
				obj2 = (object)val2;
			}
			MultiplayerHooking.OnDisconnected += (ServerEvent)obj2;
			object obj3 = <>O.<2>__OnFusionPlayerJoined;
			if (obj3 == null)
			{
				PlayerUpdate val3 = OnFusionPlayerJoined;
				<>O.<2>__OnFusionPlayerJoined = val3;
				obj3 = (object)val3;
			}
			MultiplayerHooking.OnPlayerJoined += (PlayerUpdate)obj3;
			object obj4 = <>O.<3>__OnFusionPlayerLeft;
			if (obj4 == null)
			{
				PlayerUpdate val4 = OnFusionPlayerLeft;
				<>O.<3>__OnFusionPlayerLeft = val4;
				obj4 = (object)val4;
			}
			MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj4;
			NetworkPlayer.OnNetworkPlayerRegistered += OnNetworkPlayerRegistered;
			if (NetworkInfo.HasServer)
			{
				OnFusionJoinedServer();
			}
			MelonLogger.Msg($"Fusion Personal Block v{"1.0.1"} ready with {_settings.BlockedUsers.Count} saved blocked player(s). All effects are local-only.");
		}

		public override void OnDeinitializeMelon()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			object obj = <>O.<0>__OnFusionJoinedServer;
			if (obj == null)
			{
				ServerEvent val = OnFusionJoinedServer;
				<>O.<0>__OnFusionJoinedServer = val;
				obj = (object)val;
			}
			MultiplayerHooking.OnJoinedServer -= (ServerEvent)obj;
			object obj2 = <>O.<1>__OnFusionDisconnected;
			if (obj2 == null)
			{
				ServerEvent val2 = OnFusionDisconnected;
				<>O.<1>__OnFusionDisconnected = val2;
				obj2 = (object)val2;
			}
			MultiplayerHooking.OnDisconnected -= (ServerEvent)obj2;
			object obj3 = <>O.<2>__OnFusionPlayerJoined;
			if (obj3 == null)
			{
				PlayerUpdate val3 = OnFusionPlayerJoined;
				<>O.<2>__OnFusionPlayerJoined = val3;
				obj3 = (object)val3;
			}
			MultiplayerHooking.OnPlayerJoined -= (PlayerUpdate)obj3;
			object obj4 = <>O.<3>__OnFusionPlayerLeft;
			if (obj4 == null)
			{
				PlayerUpdate val4 = OnFusionPlayerLeft;
				<>O.<3>__OnFusionPlayerLeft = val4;
				obj4 = (object)val4;
			}
			MultiplayerHooking.OnPlayerLeft -= (PlayerUpdate)obj4;
			NetworkPlayer.OnNetworkPlayerRegistered -= OnNetworkPlayerRegistered;
			RestoreAllLocalEffects();
		}

		public override void OnUpdate()
		{
			Action result;
			while (MainThreadActions.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception value)
				{
					MelonLogger.Error($"Fusion Personal Block action failed: {value}");
				}
			}
			_effectTimer += Time.unscaledDeltaTime;
			if (!(_effectTimer < 0.25f))
			{
				_effectTimer = 0f;
				ApplyAllBlockedEffects();
			}
		}

		private static void BuildBoneMenu()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			Page obj = Page.Root.CreatePage("Fusion Personal Block", Color.magenta, 0, true);
			obj.CreateFunction("Show selected player", Color.white, (Action)ShowSelectedPlayer);
			_lobbyPlayersPage = obj.CreatePage("Select lobby player", Color.cyan, 0, true);
			RefreshLobbyPlayersPage();
			obj.CreateFunction("Refresh lobby players", Color.cyan, (Action)RefreshPlayerPages);
			obj.CreateFunction("BLOCK selected player (press twice)", Color.red, (Action)BlockSelectedPlayer);
			obj.CreateFunction("UNBLOCK selected player (press twice)", Color.green, (Action)UnblockSelectedPlayer);
			obj.CreateFunction("Clear pending confirmation", Color.gray, (Action)delegate
			{
				ClearConfirmation();
			});
			_blockedPlayersPage = obj.CreatePage("Saved blocked players", Color.red, 0, true);
			RefreshBlockedPlayersPage();
			Page obj2 = obj.CreatePage("Local block settings", Color.yellow, 0, true);
			obj2.CreateBool("Mute blocked players", Color.green, _settings.MuteBlockedPlayers, (Action<bool>)SetMuteBlockedPlayers);
			obj2.CreateBool("Hide blocked players", Color.green, _settings.HideBlockedPlayers, (Action<bool>)SetHideBlockedPlayers);
			obj2.CreateBool("Disable rig grabbing", Color.green, _settings.DisableRigGrabbing, (Action<bool>)SetDisableRigGrabbing);
			obj2.CreateBool("Auto-leave blocked player", Color.red, _settings.AutoLeaveBlockedPlayer, (Action<bool>)SetAutoLeaveBlockedPlayer);
			obj2.CreateFunction("Apply local effects now", Color.cyan, (Action)ApplyEffectsNow);
			obj2.CreateFunction("Leave current Fusion lobby", Color.red, (Action)LeaveCurrentLobby);
			obj.CreateFunction("Show block status", Color.white, (Action)ShowBlockStatus);
			obj.CreateFunction("Open saved-list folder", Color.gray, (Action)OpenDataFolder);
		}

		private static void LoadSettings()
		{
			_settings = new PersonalBlockSettings();
			if (!File.Exists(_settingsPath))
			{
				return;
			}
			try
			{
				PersonalBlockSettings personalBlockSettings = JsonSerializer.Deserialize<PersonalBlockSettings>(File.ReadAllText(_settingsPath), new JsonSerializerOptions
				{
					PropertyNameCaseInsensitive = true
				});
				if (personalBlockSettings == null || personalBlockSettings.SchemaVersion != 1)
				{
					throw new InvalidDataException("schemaVersion must be 1.");
				}
				PersonalBlockSettings personalBlockSettings2 = personalBlockSettings;
				if (personalBlockSettings2.BlockedUsers == null)
				{
					List<PersonalBlockedUser> list = (personalBlockSettings2.BlockedUsers = new List<PersonalBlockedUser>());
				}
				personalBlockSettings.BlockedUsers = (from @group in personalBlockSettings.BlockedUsers.Where((PersonalBlockedUser user) => ulong.TryParse(user.SteamId, out var result) && result != 0).GroupBy<PersonalBlockedUser, string>((PersonalBlockedUser user) => user.SteamId, StringComparer.Ordinal)
					select @group.First()).Take(500).ToList();
				_settings = personalBlockSettings;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Could not load the personal block list: " + ex.Message);
			}
		}

		private static void SaveSettings()
		{
			try
			{
				_settings.SchemaVersion = 1;
				string contents = JsonSerializer.Serialize(_settings, new JsonSerializerOptions
				{
					WriteIndented = true
				});
				string text = _settingsPath + ".new";
				File.WriteAllText(text, contents);
				File.Move(text, _settingsPath, overwrite: true);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Could not save the personal block list: {value}");
				Warn("The personal block list could not be saved. Check MelonLoader logs.");
			}
		}

		private static bool IsBlocked(ulong steamId)
		{
			ulong result;
			return _settings.BlockedUsers.Any((PersonalBlockedUser user) => ulong.TryParse(user.SteamId, out result) && result == steamId);
		}

		private static PersonalBlockedUser? FindBlockedUser(ulong steamId)
		{
			ulong result;
			return _settings.BlockedUsers.FirstOrDefault((PersonalBlockedUser user) => ulong.TryParse(user.SteamId, out result) && result == steamId);
		}

		private static NetworkPlayer? FindNetworkPlayer(ulong steamId)
		{
			return ((IEnumerable<NetworkPlayer>)NetworkPlayer.Players).FirstOrDefault((Func<NetworkPlayer, bool>)((NetworkPlayer player) => player.PlayerID != null && player.PlayerID.IsValid && player.PlayerID.PlatformID == steamId));
		}

		private static void OnFusionJoinedServer()
		{
			_autoLeaveQueued = false;
			MainThreadActions.Enqueue(delegate
			{
				RefreshPlayerPages();
				NetworkPlayer val = ((IEnumerable<NetworkPlayer>)NetworkPlayer.Players).FirstOrDefault((Func<NetworkPlayer, bool>)((NetworkPlayer player) => player.PlayerID != null && !player.PlayerID.IsMe && IsBlocked(player.PlayerID.PlatformID)));
				if (((val != null) ? val.PlayerID : null) != null && _settings.AutoLeaveBlockedPlayer)
				{
					Notify("Blocked player detected", val.Username + " is in this lobby. Leaving locally.", (NotificationType)1, 6f);
					QueueAutoLeave(val.PlayerID);
				}
				else
				{
					ApplyAllBlockedEffects();
				}
			});
		}

		private static void OnFusionDisconnected()
		{
			MainThreadActions.Enqueue(delegate
			{
				RestoreAllLocalEffects();
				_autoLeaveQueued = false;
				RefreshPlayerPages();
			});
		}

		private static void OnFusionPlayerJoined(PlayerID playerId)
		{
			if (playerId == null || playerId.IsMe || !IsBlocked(playerId.PlatformID))
			{
				return;
			}
			MainThreadActions.Enqueue(delegate
			{
				string playerName = GetPlayerName(playerId.PlatformID);
				if (_settings.AutoLeaveBlockedPlayer)
				{
					Notify("Blocked player joined", playerName + " joined. Leaving this lobby locally.", (NotificationType)1, 6f);
					QueueAutoLeave(playerId);
				}
				else
				{
					ApplyBlockedEffects(playerId.PlatformID);
					Notify("Blocked player joined", "Local mute, hide, and interaction settings were applied to " + playerName + ".", (NotificationType)0, 6f);
				}
				RefreshPlayerPages();
			});
		}

		private static void OnFusionPlayerLeft(PlayerID playerId)
		{
			if (playerId != null)
			{
				ulong steamId = playerId.PlatformID;
				MainThreadActions.Enqueue(delegate
				{
					RestorePlayerEffects(steamId);
					RefreshPlayerPages();
				});
			}
		}

		private static void OnNetworkPlayerRegistered(NetworkPlayer player)
		{
			if (((player != null) ? player.PlayerID : null) != null && !player.PlayerID.IsMe && IsBlocked(player.PlayerID.PlatformID))
			{
				ulong steamId = player.PlayerID.PlatformID;
				MainThreadActions.Enqueue(delegate
				{
					ApplyBlockedEffects(steamId);
				});
			}
		}

		private static void ApplyAllBlockedEffects()
		{
			if (!NetworkInfo.HasServer)
			{
				return;
			}
			NetworkPlayer[] array = NetworkPlayer.Players.ToArray();
			foreach (NetworkPlayer val in array)
			{
				if (val.PlayerID != null && !val.PlayerID.IsMe)
				{
					if (IsBlocked(val.PlayerID.PlatformID))
					{
						ApplyBlockedEffects(val.PlayerID.PlatformID);
					}
					else
					{
						RestorePlayerEffects(val.PlayerID.PlatformID);
					}
				}
			}
		}

		private static void ApplyBlockedEffects(ulong steamId)
		{
			NetworkPlayer val = FindNetworkPlayer(steamId);
			if (((val != null) ? val.PlayerID : null) == null || val.PlayerID.IsMe)
			{
				return;
			}
			if (_settings.HideBlockedPlayers)
			{
				if (!OriginalForceHide.ContainsKey(steamId))
				{
					OriginalForceHide[steamId] = val.ForceHide;
				}
				val.ForceHide = true;
			}
			else
			{
				RestoreVisibility(steamId, val);
			}
			if (_settings.MuteBlockedPlayers)
			{
				IVoiceManager voiceManager = VoiceInfo.VoiceManager;
				IVoiceSpeaker val2 = ((voiceManager != null) ? voiceManager.GetSpeaker(val.PlayerID) : null);
				if (val2 != null)
				{
					if (!OriginalVoiceVolume.ContainsKey(steamId))
					{
						OriginalVoiceVolume[steamId] = val2.Volume;
					}
					val2.Volume = 0f;
				}
				RigVoiceSource voiceSource = val.VoiceSource;
				if ((Object)(object)((voiceSource != null) ? voiceSource.VoiceSource : null) != (Object)null)
				{
					if (!OriginalVoiceMuted.ContainsKey(steamId))
					{
						OriginalVoiceMuted[steamId] = val.VoiceSource.VoiceSource.Muted;
					}
					val.VoiceSource.VoiceSource.Muted = true;
				}
			}
			else
			{
				RestoreVoice(steamId, val);
			}
			if (_settings.DisableRigGrabbing)
			{
				RigRefs rigRefs = val.RigRefs;
				if (rigRefs != null && rigRefs.IsValid)
				{
					DisableRigInteraction(steamId, val);
					return;
				}
			}
			RestoreRigInteraction(steamId);
		}

		private static void RestorePlayerEffects(ulong steamId)
		{
			NetworkPlayer player = FindNetworkPlayer(steamId);
			RestoreVisibility(steamId, player);
			RestoreVoice(steamId, player);
			RestoreRigInteraction(steamId);
		}

		private static void RestoreVisibility(ulong steamId, NetworkPlayer? player)
		{
			if (OriginalForceHide.Remove(steamId, out var value) && player != null)
			{
				player.ForceHide = value;
			}
		}

		private static void RestoreVoice(ulong steamId, NetworkPlayer? player)
		{
			if (OriginalVoiceVolume.Remove(steamId, out var value) && ((player != null) ? player.PlayerID : null) != null)
			{
				IVoiceManager voiceManager = VoiceInfo.VoiceManager;
				IVoiceSpeaker val = ((voiceManager != null) ? voiceManager.GetSpeaker(player.PlayerID) : null);
				if (val != null)
				{
					val.Volume = value;
				}
			}
			if (OriginalVoiceMuted.Remove(steamId, out var value2))
			{
				object obj;
				if (player == null)
				{
					obj = null;
				}
				else
				{
					RigVoiceSource voiceSource = player.VoiceSource;
					obj = ((voiceSource != null) ? voiceSource.VoiceSource : null);
				}
				if ((Object)obj != (Object)null)
				{
					player.VoiceSource.VoiceSource.Muted = value2;
				}
			}
		}

		private static void DisableRigInteraction(ulong steamId, NetworkPlayer player)
		{
			RigRefs rigRefs = player.RigRefs;
			if (((rigRefs != null) ? rigRefs.RigGrips : null) == null)
			{
				return;
			}
			if (!DisabledGripStates.TryGetValue(steamId, out List<GripInteractionState> value))
			{
				value = new List<GripInteractionState>();
				DisabledGripStates[steamId] = value;
			}
			Grip[] rigGrips = player.RigRefs.RigGrips;
			foreach (Grip grip in rigGrips)
			{
				if ((Object)(object)grip == (Object)null)
				{
					continue;
				}
				GripInteractionState gripInteractionState = value.FirstOrDefault((GripInteractionState saved) => (Object)(object)saved.Grip == (Object)(object)grip);
				if (gripInteractionState == null)
				{
					gripInteractionState = new GripInteractionState(grip, ((HandReciever)grip).IsEnabled);
					value.Add(gripInteractionState);
					try
					{
						foreach (Hand item in grip.attachedHands.ToArray())
						{
							if ((Object)(object)((item != null) ? item.manager : null) != (Object)null && FusionPlayer.IsLocalPlayer(item.manager))
							{
								GripExtensions.TryDetach(grip, item);
							}
						}
					}
					catch (Exception ex)
					{
						MelonLogger.Warning("Could not detach a local hand from a blocked rig: " + ex.Message);
					}
				}
				if (((HandReciever)grip).IsEnabled)
				{
					((HandReciever)grip).DisableInteraction();
				}
			}
		}

		private static void RestoreRigInteraction(ulong steamId)
		{
			if (!DisabledGripStates.Remove(steamId, out List<GripInteractionState> value))
			{
				return;
			}
			foreach (GripInteractionState item in value)
			{
				try
				{
					if (!((Object)(object)item.Grip == (Object)null))
					{
						if (item.WasEnabled)
						{
							((HandReciever)item.Grip).EnableInteraction();
						}
						else
						{
							((HandReciever)item.Grip).DisableInteraction();
						}
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Could not restore a blocked rig interaction state: " + ex.Message);
				}
			}
		}

		private static void RestoreAllLocalEffects()
		{
			ulong[] array = OriginalForceHide.Keys.Concat(OriginalVoiceVolume.Keys).Concat(OriginalVoiceMuted.Keys).Concat(DisabledGripStates.Keys)
				.Distinct()
				.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				RestorePlayerEffects(array[i]);
			}
			OriginalForceHide.Clear();
			OriginalVoiceVolume.Clear();
			OriginalVoiceMuted.Clear();
			DisabledGripStates.Clear();
		}

		private static void QueueAutoLeave(PlayerID playerId)
		{
			if (_autoLeaveQueued || playerId == null || !IsBlocked(playerId.PlatformID))
			{
				return;
			}
			_autoLeaveQueued = true;
			MainThreadActions.Enqueue(delegate
			{
				try
				{
					if (NetworkInfo.HasServer && IsBlocked(playerId.PlatformID))
					{
						NetworkInfo.Layer.Disconnect("Fusion Personal Block: blocked player present");
					}
				}
				catch (Exception value)
				{
					MelonLogger.Error($"Could not leave the Fusion lobby: {value}");
					Warn("Could not leave the Fusion lobby automatically.");
				}
				finally
				{
					_autoLeaveQueued = false;
				}
			});
		}

		private static void LeaveCurrentLobby()
		{
			if (!NetworkInfo.HasServer)
			{
				Warn("You are not connected to a Fusion lobby.");
				return;
			}
			try
			{
				NetworkInfo.Layer.Disconnect("Fusion Personal Block: local leave");
				Notify("Fusion Personal Block", "Left the Fusion lobby locally.", (NotificationType)3, 5f);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Could not leave the Fusion lobby: {value}");
				Warn("Could not leave the current Fusion lobby.");
			}
		}

		private static void RefreshPlayerPages()
		{
			RefreshLobbyPlayersPage();
			RefreshBlockedPlayersPage();
		}

		private static void RefreshLobbyPlayersPage()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			if (_lobbyPlayersPage == null)
			{
				return;
			}
			_lobbyPlayersPage.RemoveAll();
			NetworkPlayer[] array = NetworkPlayer.Players.Where((NetworkPlayer player) => player.PlayerID != null && player.PlayerID.IsValid && !player.PlayerID.IsMe).OrderBy<NetworkPlayer, string>((NetworkPlayer player) => player.Username, StringComparer.OrdinalIgnoreCase).ToArray();
			if (!NetworkInfo.HasServer || array.Length == 0)
			{
				_lobbyPlayersPage.CreateFunction("No other Fusion players found", Color.gray, (Action)delegate
				{
					Warn("Join a Fusion lobby with another player, then refresh.");
				});
				return;
			}
			NetworkPlayer[] array2 = array;
			foreach (NetworkPlayer val in array2)
			{
				ulong steamId = val.PlayerID.PlatformID;
				string name = (string.IsNullOrWhiteSpace(val.Username) ? $"Steam {steamId}" : val.Username);
				bool flag = IsBlocked(steamId);
				_lobbyPlayersPage.CreateFunction(ShortText((flag ? "[BLOCKED]" : "[ALLOWED]") + " " + name, 54), flag ? Color.red : Color.green, (Action)delegate
				{
					SelectPlayer(steamId, name);
				});
			}
		}

		private static void RefreshBlockedPlayersPage()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			if (_blockedPlayersPage == null)
			{
				return;
			}
			_blockedPlayersPage.RemoveAll();
			if (_settings.BlockedUsers.Count == 0)
			{
				_blockedPlayersPage.CreateFunction("No saved blocked players", Color.gray, (Action)delegate
				{
					Notify("Fusion Personal Block", "Select a lobby player and block them first.", (NotificationType)0, 5f);
				});
				return;
			}
			foreach (PersonalBlockedUser item in _settings.BlockedUsers.OrderBy<PersonalBlockedUser, string>((PersonalBlockedUser user) => user.DisplayName, StringComparer.OrdinalIgnoreCase))
			{
				if (ulong.TryParse(item.SteamId, out var steamId))
				{
					string name = (string.IsNullOrWhiteSpace(item.DisplayName) ? $"Steam {steamId}" : item.DisplayName);
					_blockedPlayersPage.CreateFunction(ShortText("[BLOCKED] " + name, 54), Color.red, (Action)delegate
					{
						SelectPlayer(steamId, name);
					});
				}
			}
		}

		private static void SelectPlayer(ulong steamId, string name)
		{
			if (steamId == PlayerIDManager.LocalPlatformID)
			{
				Warn("You cannot add your own Steam account to the personal block list.");
				return;
			}
			_selectedSteamId = steamId;
			_selectedName = ShortText(name, 80);
			ClearConfirmation(showNotification: false);
			Notify("Player selected", $"{_selectedName} ({steamId}) is {(IsBlocked(steamId) ? "BLOCKED" : "ALLOWED")}.", (NotificationType)0, 7f);
		}

		private static void ShowSelectedPlayer()
		{
			if (!_selectedSteamId.HasValue)
			{
				Warn("Select someone from the lobby list or saved blocked players first.");
				return;
			}
			Notify("Selected player", $"{_selectedName} | Steam ID {_selectedSteamId.Value} | {(IsBlocked(_selectedSteamId.Value) ? "BLOCKED" : "ALLOWED")}.", (NotificationType)0, 8f);
		}

		private static bool ConfirmAction(string action)
		{
			if (!_selectedSteamId.HasValue)
			{
				return false;
			}
			DateTimeOffset utcNow = DateTimeOffset.UtcNow;
			if (string.Equals(_pendingAction, action, StringComparison.Ordinal) && _pendingActionSteamId == _selectedSteamId && utcNow <= _pendingActionExpiresUtc)
			{
				ClearConfirmation(showNotification: false);
				return true;
			}
			_pendingAction = action;
			_pendingActionSteamId = _selectedSteamId;
			_pendingActionExpiresUtc = utcNow.AddSeconds(12.0);
			Notify("Confirm local block action", $"Press {action} again within 12 seconds for {_selectedName}.", (NotificationType)1, 8f);
			return false;
		}

		private static void ClearConfirmation(bool showNotification = true)
		{
			_pendingAction = string.Empty;
			_pendingActionSteamId = null;
			_pendingActionExpiresUtc = default(DateTimeOffset);
			if (showNotification)
			{
				Notify("Fusion Personal Block", "Pending confirmation cleared.", (NotificationType)0, 4f);
			}
		}

		private static void BlockSelectedPlayer()
		{
			if (!_selectedSteamId.HasValue)
			{
				Warn("Select a current Fusion player before blocking.");
			}
			else if (IsBlocked(_selectedSteamId.Value))
			{
				Warn("The selected player is already on your personal block list.");
			}
			else if (_settings.BlockedUsers.Count >= 500)
			{
				Warn($"The personal block list is limited to {500} entries.");
			}
			else if (ConfirmAction("BLOCK"))
			{
				_settings.BlockedUsers.Add(new PersonalBlockedUser
				{
					SteamId = _selectedSteamId.Value.ToString(),
					DisplayName = _selectedName,
					AddedUtc = DateTimeOffset.UtcNow
				});
				SaveSettings();
				ApplyBlockedEffects(_selectedSteamId.Value);
				RefreshPlayerPages();
				Notify("Player blocked locally", "Muted, hidden, and saved " + _selectedName + ". This affects only your game.", (NotificationType)3, 8f);
				NetworkPlayer val = FindNetworkPlayer(_selectedSteamId.Value);
				if (_settings.AutoLeaveBlockedPlayer && ((val != null) ? val.PlayerID : null) != null)
				{
					QueueAutoLeave(val.PlayerID);
				}
			}
		}

		private static void UnblockSelectedPlayer()
		{
			if (!_selectedSteamId.HasValue)
			{
				Warn("Select someone from Saved blocked players before unblocking.");
				return;
			}
			PersonalBlockedUser personalBlockedUser = FindBlockedUser(_selectedSteamId.Value);
			if (personalBlockedUser == null)
			{
				Warn("The selected player is not on your personal block list.");
			}
			else if (ConfirmAction("UNBLOCK"))
			{
				_settings.BlockedUsers.Remove(personalBlockedUser);
				SaveSettings();
				RestorePlayerEffects(_selectedSteamId.Value);
				RefreshPlayerPages();
				Notify("Player unblocked locally", "Restored local visibility and voice for " + _selectedName + ".", (NotificationType)3, 7f);
			}
		}

		private static void SetMuteBlockedPlayers(bool value)
		{
			_settings.MuteBlockedPlayers = value;
			SaveSettings();
			if (!value)
			{
				ulong[] array = OriginalVoiceVolume.Keys.Concat(OriginalVoiceMuted.Keys).Distinct().ToArray();
				foreach (ulong steamId in array)
				{
					RestoreVoice(steamId, FindNetworkPlayer(steamId));
				}
			}
			ApplyAllBlockedEffects();
		}

		private static void SetHideBlockedPlayers(bool value)
		{
			_settings.HideBlockedPlayers = value;
			SaveSettings();
			if (!value)
			{
				ulong[] array = OriginalForceHide.Keys.ToArray();
				foreach (ulong steamId in array)
				{
					RestoreVisibility(steamId, FindNetworkPlayer(steamId));
				}
			}
			ApplyAllBlockedEffects();
		}

		private static void SetDisableRigGrabbing(bool value)
		{
			_settings.DisableRigGrabbing = value;
			SaveSettings();
			if (!value)
			{
				ulong[] array = DisabledGripStates.Keys.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					RestoreRigInteraction(array[i]);
				}
			}
			else
			{
				ApplyAllBlockedEffects();
			}
			Notify("Fusion Personal Block", "Blocked-player rig grabbing is now " + (value ? "disabled" : "allowed") + " locally.", (NotificationType)0, 5f);
		}

		private static void SetAutoLeaveBlockedPlayer(bool value)
		{
			_settings.AutoLeaveBlockedPlayer = value;
			SaveSettings();
			Notify("Fusion Personal Block", "Automatic lobby leave is now " + (value ? "ON" : "OFF") + ".", (NotificationType)0, 5f);
			if (value && NetworkInfo.HasServer)
			{
				NetworkPlayer val = ((IEnumerable<NetworkPlayer>)NetworkPlayer.Players).FirstOrDefault((Func<NetworkPlayer, bool>)((NetworkPlayer player) => player.PlayerID != null && !player.PlayerID.IsMe && IsBlocked(player.PlayerID.PlatformID)));
				if (((val != null) ? val.PlayerID : null) != null)
				{
					QueueAutoLeave(val.PlayerID);
				}
			}
		}

		private static void ApplyEffectsNow()
		{
			ApplyAllBlockedEffects();
			Notify("Fusion Personal Block", "Local block effects reapplied to the current lobby.", (NotificationType)3, 5f);
		}

		private static void ShowBlockStatus()
		{
			int value = NetworkPlayer.Players.Count((NetworkPlayer player) => player.PlayerID != null && !player.PlayerID.IsMe && IsBlocked(player.PlayerID.PlatformID));
			Notify("Fusion Personal Block status", $"{_settings.BlockedUsers.Count} saved; {value} in this lobby. Mute {OnOff(_settings.MuteBlockedPlayers)}, hide {OnOff(_settings.HideBlockedPlayers)}, rig block {OnOff(_settings.DisableRigGrabbing)}, auto-leave {OnOff(_settings.AutoLeaveBlockedPlayer)}.", (NotificationType)0, 10f);
		}

		private static void OpenDataFolder()
		{
			try
			{
				Process.Start(new ProcessStartInfo
				{
					FileName = _dataDirectory,
					UseShellExecute = true
				});
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Could not open the personal block folder: {value}");
				Warn("Could not open the saved-list folder.");
			}
		}

		private static string GetPlayerName(ulong steamId)
		{
			NetworkPlayer val = FindNetworkPlayer(steamId);
			if (val == null || string.IsNullOrWhiteSpace(val.Username))
			{
				return FindBlockedUser(steamId)?.DisplayName ?? $"Steam {steamId}";
			}
			return val.Username;
		}

		private static string ShortText(string value, int maximum)
		{
			string text = string.Join(" ", (value ?? string.Empty).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries));
			if (text.Length > maximum)
			{
				return text.Substring(0, Math.Max(1, maximum - 1)) + "…";
			}
			return text;
		}

		private static string OnOff(bool value)
		{
			if (!value)
			{
				return "OFF";
			}
			return "ON";
		}

		private static void Warn(string message)
		{
			MelonLogger.Warning(message);
			Notify("Fusion Personal Block", message, (NotificationType)1, 6f);
		}

		private static void Notify(string title, string message, NotificationType type, float seconds)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			Notifier.Send(new Notification
			{
				Title = new NotificationText(title),
				Message = new NotificationText(message),
				ShowTitleOnPopup = true,
				PopupLength = seconds,
				Type = type
			});
		}
	}
	public sealed class PersonalBlockSettings
	{
		public int SchemaVersion { get; set; } = 1;

		public bool MuteBlockedPlayers { get; set; } = true;

		public bool HideBlockedPlayers { get; set; } = true;

		public bool DisableRigGrabbing { get; set; } = true;

		public bool AutoLeaveBlockedPlayer { get; set; } = true;

		public List<PersonalBlockedUser> BlockedUsers { get; set; } = new List<PersonalBlockedUser>();
	}
	public sealed class PersonalBlockedUser
	{
		public string SteamId { get; set; } = string.Empty;

		public string DisplayName { get; set; } = string.Empty;

		public DateTimeOffset AddedUtc { get; set; }
	}
	public sealed class GripInteractionState
	{
		public Grip Grip { get; }

		public bool WasEnabled { get; }

		public GripInteractionState(Grip grip, bool wasEnabled)
		{
			Grip = grip;
			WasEnabled = wasEnabled;
		}
	}
}