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 <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 <9>__SetMuteBlockedPlayers; public static Action <10>__SetHideBlockedPlayers; public static Action <11>__SetDisableRigGrabbing; public static Action <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 MainThreadActions = new ConcurrentQueue(); private static readonly Dictionary OriginalForceHide = new Dictionary(); private static readonly Dictionary OriginalVoiceVolume = new Dictionary(); private static readonly Dictionary OriginalVoiceMuted = new Dictionary(); private static readonly Dictionary> DisabledGripStates = new Dictionary>(); 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)SetMuteBlockedPlayers); obj2.CreateBool("Hide blocked players", Color.green, _settings.HideBlockedPlayers, (Action)SetHideBlockedPlayers); obj2.CreateBool("Disable rig grabbing", Color.green, _settings.DisableRigGrabbing, (Action)SetDisableRigGrabbing); obj2.CreateBool("Auto-leave blocked player", Color.red, _settings.AutoLeaveBlockedPlayer, (Action)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(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 list = (personalBlockSettings2.BlockedUsers = new List()); } personalBlockSettings.BlockedUsers = (from @group in personalBlockSettings.BlockedUsers.Where((PersonalBlockedUser user) => ulong.TryParse(user.SteamId, out var result) && result != 0).GroupBy((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.Players).FirstOrDefault((Func)((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.Players).FirstOrDefault((Func)((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 value)) { value = new List(); 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 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 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 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.Players).FirstOrDefault((Func)((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 BlockedUsers { get; set; } = new List(); } 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; } } }