using System; 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.Security; using System.Security.Permissions; using BoneLib; using BoneLib.BoneMenu; using HarmonyLib; using Il2CppSLZ.Marrow.Warehouse; using LabFusion.Data; using LabFusion.Network; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using QuickBlacklist; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(QuickBlacklistMod), "QuickBlacklist", "1.1.0", "nontendo", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("QuickBlacklist")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("QuickBlacklist")] [assembly: AssemblyTitle("QuickBlacklist")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] 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; } } } namespace QuickBlacklist { public static class BlacklistMenu { private const int MaxResults = 40; private static Page? _resultsHost; private static readonly List _resultElements = new List(); private static string _lastQuery = ""; private static Page? _managePage; private static readonly List _manageElements = new List(); private static Page? _keywordPage; private static readonly List _keywordElements = new List(); public static void Build(Page root) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) root.CreateBool("Enabled", Color.white, BlacklistSettings.Enabled, (Action)delegate(bool v) { BlacklistSettings.Enabled = v; }); Page? obj = (_resultsHost = root.CreatePage("Search & Add", new Color(0.2f, 0.85f, 0.4f), 0, true)); obj.CreateBool("Search Avatars Instead of Items", new Color(1f, 0.55f, 0.85f), BlacklistSettings.SearchAvatars, (Action)delegate(bool v) { BlacklistSettings.SearchAvatars = v; RefreshResults(_lastQuery); }); obj.CreateString("Search", Color.white, "", (Action)OnSearchChanged); _managePage = root.CreatePage("Manage Blacklist", new Color(0.9f, 0.35f, 0.15f), 0, true); RefreshManagePage(); _keywordPage = root.CreatePage("Keyword Blacklist", new Color(0.9f, 0.6f, 0.1f), 0, true); _keywordPage.CreateString("Add Keyword", Color.white, "", (Action)OnAddKeyword); RefreshKeywordPage(); } private static void OnAddKeyword(string keyword) { KeywordBlockList.Add(keyword); RefreshKeywordPage(); } private static void RefreshKeywordPage() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (_keywordPage == null) { return; } if (_keywordElements.Count > 0) { _keywordPage.Remove(_keywordElements.ToArray()); _keywordElements.Clear(); } if (KeywordBlockList.Keywords.Count == 0) { _keywordElements.Add((Element)(object)_keywordPage.CreateFunction("(no keywords set)", Color.gray, (Action)delegate { })); return; } foreach (string keyword in KeywordBlockList.Keywords) { string captured = keyword; Element item = (Element)(object)_keywordPage.CreateFunction("Remove: " + keyword, Color.red, (Action)delegate { KeywordBlockList.Remove(captured); RefreshKeywordPage(); }); _keywordElements.Add(item); } } private static void OnSearchChanged(string query) { RefreshResults(query); } private static void RefreshResults(string query) { //IL_0083: 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_012d: Unknown result type (might be due to invalid IL or missing references) if (_resultsHost == null) { return; } _lastQuery = query; if (_resultElements.Count > 0) { _resultsHost.Remove(_resultElements.ToArray()); _resultElements.Clear(); } if (string.IsNullOrWhiteSpace(query)) { return; } List<(string, string)> list = FindMatches(query.Trim(), 40); if (list.Count == 0) { _resultElements.Add((Element)(object)_resultsHost.CreateFunction("(no matches)", Color.gray, (Action)delegate { })); return; } foreach (var item4 in list) { string item = item4.Item1; string item2 = item4.Item2; string capturedBarcode = item2; string capturedTitle = item; bool flag = BlacklistStore.IsBlacklisted(item2) || KeywordBlockList.IsBlocked(item2); Element item3 = (Element)(object)_resultsHost.CreateFunction(flag ? ("[Blocked] " + item) : ("+ " + item), flag ? Color.gray : Color.green, (Action)delegate { if (!BlacklistStore.IsBlacklisted(capturedBarcode) && !KeywordBlockList.IsBlocked(capturedBarcode)) { BlacklistStore.Add(capturedBarcode, capturedTitle); RefreshResults(query); RefreshManagePage(); } }); _resultElements.Add(item3); } } private static List<(string Title, string Barcode)> FindMatches(string query, int maxResults) { List<(string, string)> list = new List<(string, string)>(); try { bool searchAvatars = BlacklistSettings.SearchAvatars; string value = query.ToLowerInvariant(); foreach (SpawnableCrate item in Resources.FindObjectsOfTypeAll()) { if ((Object)(object)item == (Object)null || item is AvatarCrate != searchAvatars) { continue; } string text = ((Scannable)item).Title ?? "Unknown"; Barcode barcode = ((Scannable)item).Barcode; string text2 = ((barcode != null) ? barcode.ID : null) ?? ""; if (!string.IsNullOrEmpty(text2) && (text.ToLowerInvariant().Contains(value) || text2.ToLowerInvariant().Contains(value))) { list.Add((text, text2)); if (list.Count >= maxResults) { break; } } } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Warehouse search failed: " + ex.Message); } return list; } private static void RefreshManagePage() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (_managePage == null) { return; } if (_manageElements.Count > 0) { _managePage.Remove(_manageElements.ToArray()); _manageElements.Clear(); } List<(string, string)> entries = BlacklistStore.GetEntries(); if (entries.Count == 0) { _manageElements.Add((Element)(object)_managePage.CreateFunction("(nothing blacklisted yet)", Color.gray, (Action)delegate { })); return; } foreach (var item4 in entries) { string item = item4.Item1; string item2 = item4.Item2; string capturedBarcode = item; Element item3 = (Element)(object)_managePage.CreateFunction("Remove: " + item2, Color.red, (Action)delegate { BlacklistStore.Remove(capturedBarcode); RefreshManagePage(); }); _manageElements.Add(item3); } } } [HarmonyPatch(typeof(HelperMethods), "SpawnCrate", new Type[] { typeof(SpawnableCrateReference), typeof(Vector3), typeof(Quaternion), typeof(Vector3), typeof(bool), typeof(Action), typeof(Action) })] public static class BlacklistPatches { [HarmonyPrefix] public static bool Prefix(SpawnableCrateReference crateReference) { return !ShouldBlock(crateReference); } private static bool ShouldBlock(SpawnableCrateReference crateReference) { if (!BlacklistSettings.Enabled) { return false; } try { object obj; if (crateReference == null) { obj = null; } else { Barcode barcode = ((ScannableReference)crateReference).Barcode; obj = ((barcode != null) ? barcode.ID : null); } string text = (string)obj; if (string.IsNullOrEmpty(text)) { return false; } int num; if (!BlacklistStore.IsBlacklisted(text)) { num = (KeywordBlockList.IsBlocked(text) ? 1 : 0); if (num == 0) { goto IL_0053; } } else { num = 1; } MelonLogger.Msg("[QuickBlacklist] Blocked local spawn of blacklisted barcode: " + text); goto IL_0053; IL_0053: return (byte)num != 0; } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Blacklist check failed, allowing spawn: " + ex.Message); return false; } } } public static class BlacklistSettings { private static MelonPreferences_Category _cat; private static MelonPreferences_Entry _enabled; private static MelonPreferences_Entry _searchAvatars; public static bool Enabled { get { return _enabled.Value; } set { _enabled.Value = value; _cat.SaveToFile(false); } } public static bool SearchAvatars { get { return _searchAvatars.Value; } set { _searchAvatars.Value = value; _cat.SaveToFile(false); } } public static void Initialize() { _cat = MelonPreferences.CreateCategory("QuickBlacklist"); _enabled = _cat.CreateEntry("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); _searchAvatars = _cat.CreateEntry("SearchAvatars", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); } } public static class BlacklistStore { private static string FilePath => ModBlacklist.GetFilePath(); public static bool IsBlacklisted(string barcode) { if (string.IsNullOrEmpty(barcode)) { return false; } try { return ModBlacklist.IsBlacklisted(barcode); } catch { return false; } } public static List<(string Barcode, string Title)> GetEntries() { List<(string, string)> list = new List<(string, string)>(); try { foreach (string item in ReadBarcodeLines()) { list.Add((item, ResolveTitle(item))); } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Failed reading blacklist: " + ex.Message); } return list; } public static void Add(string barcode, string titleForLog) { if (string.IsNullOrWhiteSpace(barcode)) { return; } try { List list = ReadAllLinesSafe(); if (!list.Any((string l) => l.Trim() == barcode)) { list.Add(barcode); File.WriteAllLines(FilePath, list); ModBlacklist.ReadFile(); MelonLogger.Msg($"[QuickBlacklist] Blacklisted '{titleForLog}' ({barcode})"); } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Failed to add to blacklist: " + ex.Message); } } public static void Remove(string barcode) { try { List list = ReadAllLinesSafe(); if (list.RemoveAll((string l) => l.Trim() == barcode) != 0) { File.WriteAllLines(FilePath, list); ModBlacklist.ReadFile(); MelonLogger.Msg("[QuickBlacklist] Removed " + barcode + " from the blacklist"); } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Failed to remove from blacklist: " + ex.Message); } } private static List ReadAllLinesSafe() { ModBlacklist.CreateFile(); return File.ReadAllLines(FilePath).ToList(); } private static List ReadBarcodeLines() { List list = new List(); foreach (string item in ReadAllLinesSafe()) { string text = item.Trim(); if (text.Length != 0 && !text.StartsWith("#")) { list.Add(text); } } return list; } public static string ResolveTitle(string barcode) { try { foreach (SpawnableCrate item in Resources.FindObjectsOfTypeAll()) { if (!((Object)(object)item == (Object)null)) { Barcode barcode2 = ((Scannable)item).Barcode; if (((barcode2 != null) ? barcode2.ID : null) == barcode) { return ((Scannable)item).Title ?? barcode; } } } } catch { } return barcode; } } public static class KeywordBlockList { private const char Delimiter = '|'; private static MelonPreferences_Category _cat = null; private static MelonPreferences_Entry _keywordsRaw = null; private static List _keywords = new List(); public static IReadOnlyList Keywords => _keywords; public static void Initialize() { _cat = MelonPreferences.CreateCategory("QuickBlacklistKeywords"); _keywordsRaw = _cat.CreateEntry("Keywords", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null); Load(); } private static void Load() { _keywords = (from k in _keywordsRaw.Value.Split('|') select k.Trim() into k where k.Length > 0 select k).ToList(); } private static void Save() { _keywordsRaw.Value = string.Join('|'.ToString(), _keywords); _cat.SaveToFile(false); } public static void Add(string keyword) { keyword = (keyword ?? "").Trim(); if (keyword.Length != 0 && !_keywords.Any((string k) => k.Equals(keyword, StringComparison.OrdinalIgnoreCase))) { _keywords.Add(keyword); Save(); } } public static void Remove(string keyword) { if (_keywords.RemoveAll((string k) => k.Equals(keyword, StringComparison.OrdinalIgnoreCase)) > 0) { Save(); } } public static bool IsBlocked(string barcode) { if (string.IsNullOrEmpty(barcode) || _keywords.Count == 0) { return false; } try { string text = BlacklistStore.ResolveTitle(barcode); foreach (string keyword in _keywords) { if (keyword.Length != 0) { if (barcode.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (text.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } } catch { } return false; } } [HarmonyPatch(typeof(SpawnResponseMessage), "OnHandleMessage")] public static class SpawnKeywordPatch { [HarmonyPrefix] public static bool Prefix(ReceivedMessage received) { try { string barcode = ((ReceivedMessage)(ref received)).ReadData().SpawnData.Barcode; if (KeywordBlockList.IsBlocked(barcode)) { MelonLogger.Msg("[QuickBlacklist] Blocked networked spawn by keyword match: " + barcode); return false; } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Keyword spawn check failed: " + ex.Message); } return true; } } [HarmonyPatch(typeof(PlayerRepAvatarMessage), "OnHandleMessage")] public static class AvatarKeywordPatch { [HarmonyPrefix] public static bool Prefix(ReceivedMessage received) { try { PlayerRepAvatarData val = ((ReceivedMessage)(ref received)).ReadData(); if (KeywordBlockList.IsBlocked(val.Barcode)) { MelonLogger.Msg("[QuickBlacklist] Blocked networked avatar by keyword match: " + val.Barcode); return false; } } catch (Exception ex) { MelonLogger.Warning("[QuickBlacklist] Keyword avatar check failed: " + ex.Message); } return true; } } public class QuickBlacklistMod : MelonMod { public override void OnInitializeMelon() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) BlacklistSettings.Initialize(); KeywordBlockList.Initialize(); BlacklistMenu.Build(Page.Root.CreatePage("QuickBlacklist", new Color(0.85f, 0.15f, 0.15f), 0, true)); ((MelonBase)this).LoggerInstance.Msg("QuickBlacklist loaded!"); } } }