Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WorkbenchCraftSearch v1.0.2
WorkbenchCraftSearch.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: AssemblyTitle("WorkbenchCraftSearch")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("1.0.2.0")] namespace WorkbenchCraftSearch; [BepInPlugin("local.valheim.workbenchcraftsearch", "WorkbenchCraftSearch", "1.0.2")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "local.valheim.workbenchcraftsearch"; public const string Version = "1.0.2"; internal static Plugin Instance; internal ConfigEntry<float> Width; internal ConfigEntry<float> Height; internal ConfigEntry<float> OffsetX; internal ConfigEntry<float> OffsetY; private Harmony harmony; private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown Instance = this; Width = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Width", 130f, new ConfigDescription("Search width in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(80f, 240f), new object[0])); Height = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Height", 28f, new ConfigDescription("Search height in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 40f), new object[0])); OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "OffsetX", 0f, "Horizontal adjustment in UI units."); OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "OffsetY", 0f, "Vertical adjustment in UI units (positive = up)."); try { SearchController.ValidateBindings(); harmony = new Harmony("local.valheim.workbenchcraftsearch"); harmony.PatchAll(typeof(Plugin).Assembly); if ((Object)(object)InventoryGui.instance != (Object)null) { SearchController.Attach(InventoryGui.instance); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"WorkbenchCraftSearch 1.0.2 loaded."); } catch (Exception ex) { if (harmony != null) { harmony.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not enable craft search: " + ex)); } } private void OnDestroy() { if ((Object)(object)SearchController.Current != (Object)null) { Object.Destroy((Object)(object)SearchController.Current); } if (harmony != null) { harmony.UnpatchSelf(); } Instance = null; } internal static void Report(Exception error) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogError((object)error); } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryAwakePatch { private static void Postfix(InventoryGui __instance) { SearchController.Attach(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Show", new Type[] { typeof(Container), typeof(int) })] internal static class InventoryShowPatch { private static void Prefix(InventoryGui __instance) { SearchController.Attach(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel", new Type[] { typeof(bool) })] internal static class CraftingPanelPatch { private static void Prefix(InventoryGui __instance) { SearchController.Attach(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList", new Type[] { typeof(List<Recipe>) })] internal static class RecipeListPatch { [HarmonyPriority(0)] private static void Prefix(InventoryGui __instance, ref List<Recipe> __0) { SearchController current = SearchController.Current; if ((Object)(object)current == (Object)null || !current.Owns(__instance) || current.Query.IsEmpty || __0 == null) { return; } SearchQuery query = current.Query; __0 = __0.FindAll(delegate(Recipe recipe) { if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || recipe.m_item.m_itemData == null) { return false; } string name = recipe.m_item.m_itemData.m_shared.m_name; return query.Matches(Localization.instance.Localize(name)); }); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] internal static class InventoryHidePatch { private static void Prefix(InventoryGui __instance) { SearchController current = SearchController.Current; if ((Object)(object)current != (Object)null && current.Owns(__instance)) { current.Clear(); } } } [HarmonyPatch(typeof(Chat), "HasFocus")] internal static class ChatFocusPatch { private static void Postfix(ref bool __result) { __result = __result || SearchController.BlockGameInput; } } public sealed class SearchController : MonoBehaviour { private static readonly MethodInfo Refresh = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", new Type[1] { typeof(bool) }, (Type[])null); private static readonly FieldInfo CraftTimer = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); internal static SearchController Current; internal SearchQuery Query = new SearchQuery(""); private InventoryGui gui; private TMP_InputField input; private TMP_Text placeholder; private GameObject root; private RectTransform rect; private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4]; private int blockThroughFrame = -1; private bool dirty; private bool finishEditingRequested; private bool initializationFailed; private string language; internal static bool BlockGameInput { get { SearchController current = Current; if ((Object)(object)current != (Object)null && (Object)(object)current.input != (Object)null && ((Component)current.input).gameObject.activeInHierarchy) { if (!current.input.isFocused) { return Time.frameCount <= current.blockThroughFrame; } return true; } return false; } } internal static void ValidateBindings() { if (Refresh == null || CraftTimer == null) { throw new MissingMemberException("Unsupported InventoryGui: UpdateCraftingPanel or m_craftTimer is missing."); } } internal static void Attach(InventoryGui inventory) { if ((Object)(object)inventory == (Object)null || (Object)(object)inventory.m_crafting == (Object)null || (Object)(object)inventory.m_craftingStationName == (Object)null) { return; } SearchController searchController = ((Component)inventory).GetComponent<SearchController>(); if ((Object)(object)searchController == (Object)null) { searchController = ((Component)inventory).gameObject.AddComponent<SearchController>(); } if (searchController.initializationFailed) { return; } if ((Object)(object)searchController.root != (Object)null && (Object)(object)searchController.rect != (Object)null && (Object)(object)searchController.input != (Object)null && (Object)(object)((Transform)searchController.rect).parent == (Object)(object)inventory.m_crafting) { if (!searchController.root.activeSelf) { searchController.root.SetActive(true); } return; } string text = (((Object)(object)searchController.input != (Object)null) ? searchController.input.text : ""); if ((Object)(object)searchController.root != (Object)null) { searchController.root.SetActive(false); Object.Destroy((Object)(object)searchController.root); } try { searchController.Initialize(inventory); searchController.input.SetTextWithoutNotify(text); searchController.Query = new SearchQuery(text); if (!searchController.Query.IsEmpty) { searchController.dirty = true; } ((Behaviour)searchController).enabled = true; } catch (Exception error) { searchController.initializationFailed = true; ((Behaviour)searchController).enabled = false; if ((Object)(object)Current == (Object)(object)searchController) { Current = null; } if ((Object)(object)searchController.root != (Object)null) { Object.Destroy((Object)(object)searchController.root); } Plugin.Report(error); } } internal bool Owns(InventoryGui inventory) { return (Object)(object)gui == (Object)(object)inventory; } private void Initialize(InventoryGui inventory) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02da: 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) gui = inventory; if ((Object)(object)gui.m_crafting == (Object)null || (Object)(object)gui.m_craftingStationName == (Object)null) { throw new MissingMemberException("The crafting panel is unavailable."); } Current = this; root = new GameObject("WorkbenchCraftSearch", new Type[1] { typeof(RectTransform) }); root.SetActive(false); rect = (RectTransform)root.transform; ((Transform)rect).SetParent((Transform)(object)gui.m_crafting, false); RectTransform obj = rect; Vector2 anchorMin = (rect.anchorMax = new Vector2(0f, 1f)); obj.anchorMin = anchorMin; rect.pivot = new Vector2(0f, 0.5f); Image val2 = root.AddComponent<Image>(); Image val3 = (((Object)(object)gui.m_tabCraft != (Object)null) ? ((Component)gui.m_tabCraft).GetComponent<Image>() : null); if ((Object)(object)val3 != (Object)null) { val2.sprite = val3.sprite; val2.type = val3.type; } ((Graphic)val2).color = new Color(0.18f, 0.15f, 0.14f, 0.95f); ((Graphic)val2).raycastTarget = true; RectTransform val4 = NewRect("Viewport", (Transform)(object)rect); Stretch(val4, 7f, 5f); ((Component)val4).gameObject.AddComponent<RectMask2D>(); TMP_Text textComponent = NewText("Text", (Transform)(object)val4, gui.m_craftingStationName); placeholder = NewText("Placeholder", (Transform)(object)val4, gui.m_craftingStationName); ((Graphic)placeholder).color = new Color(0.8f, 0.76f, 0.68f, 0.7f); placeholder.fontStyle = (FontStyles)2; input = root.AddComponent<TMP_InputField>(); ((Selectable)input).targetGraphic = (Graphic)(object)val2; input.textViewport = val4; input.textComponent = textComponent; input.placeholder = (Graphic)(object)placeholder; input.lineType = (LineType)0; input.contentType = (ContentType)0; input.characterLimit = 80; input.richText = false; input.restoreOriginalTextOnEscape = false; input.customCaretColor = true; input.caretColor = new Color(1f, 0.83f, 0.25f); input.selectionColor = new Color(1f, 0.76f, 0.15f, 0.3f); Navigation navigation = ((Selectable)input).navigation; ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)input).navigation = navigation; ((UnityEvent<string>)(object)input.onValueChanged).AddListener((UnityAction<string>)OnChanged); ((UnityEvent<string>)(object)input.onSelect).AddListener((UnityAction<string>)delegate { blockThroughFrame = Time.frameCount + 1; }); ((UnityEvent<string>)(object)input.onDeselect).AddListener((UnityAction<string>)delegate { blockThroughFrame = Time.frameCount + 1; }); ((UnityEvent<string>)(object)input.onEndEdit).AddListener((UnityAction<string>)delegate { RequestFinishEditing(); }); ((UnityEvent<string>)(object)input.onSubmit).AddListener((UnityAction<string>)delegate { RequestFinishEditing(); }); UpdateLanguage(); PositionField(); root.SetActive(true); } private static RectTransform NewRect(string name, Transform parent) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent(parent, false); return val; } private static void Stretch(RectTransform target, float x, float y) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) target.anchorMin = Vector2.zero; target.anchorMax = Vector2.one; target.offsetMin = new Vector2(x, y); target.offsetMax = new Vector2(0f - x, 0f - y); } private static TMP_Text NewText(string name, Transform parent, TMP_Text style) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) RectTransform val = NewRect(name, parent); Stretch(val, 0f, 0f); TextMeshProUGUI val2 = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).font = style.font; ((TMP_Text)val2).fontSharedMaterial = style.fontSharedMaterial; ((TMP_Text)val2).fontSize = 16f; ((Graphic)val2).color = new Color(1f, 0.91f, 0.65f); ((TMP_Text)val2).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val2).richText = false; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).margin = Vector4.zero; return (TMP_Text)(object)val2; } private void UpdateLanguage() { string selectedLanguage = Localization.instance.GetSelectedLanguage(); if (!(selectedLanguage == language)) { language = selectedLanguage; placeholder.text = ((selectedLanguage == "Russian") ? "Поиск..." : "Search..."); if (!Query.IsEmpty) { dirty = true; } } } private void PositionField() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) Plugin instance = Plugin.Instance; if (!((Object)(object)instance == (Object)null)) { RectTransform crafting = gui.m_crafting; Rect val = crafting.rect; float num = ((Rect)(ref val)).xMin + 85f; if ((Object)(object)gui.m_craftingStationIcon != (Object)null && ((Component)gui.m_craftingStationIcon).gameObject.activeSelf) { ((Graphic)gui.m_craftingStationIcon).rectTransform.GetWorldCorners(corners); num = Mathf.Max(num, ((Transform)crafting).InverseTransformPoint(corners[2]).x + 10f); } RectTransform rectTransform = gui.m_craftingStationName.rectTransform; Rect val2 = rectTransform.rect; float y = ((Transform)crafting).InverseTransformPoint(((Transform)rectTransform).TransformPoint(Vector2.op_Implicit(((Rect)(ref val2)).center))).y; float num2 = instance.Width.Value; if (gui.m_craftingStationName.havePropertiesChanged) { gui.m_craftingStationName.ForceMeshUpdate(false, false); } Bounds textBounds = gui.m_craftingStationName.textBounds; if (((Bounds)(ref textBounds)).size.x > 0f) { float x = ((Transform)crafting).InverseTransformPoint(((Transform)rectTransform).TransformPoint(((Bounds)(ref textBounds)).min)).x; num2 = Mathf.Min(num2, Mathf.Max(80f, x - num - 12f)); } rect.sizeDelta = new Vector2(num2, instance.Height.Value); RectTransform obj = rect; float num3 = num; Rect val3 = crafting.rect; float num4 = num3 - ((Rect)(ref val3)).xMin + instance.OffsetX.Value; Rect val4 = crafting.rect; obj.anchoredPosition = new Vector2(num4, y - ((Rect)(ref val4)).yMax + instance.OffsetY.Value); } } private void OnChanged(string value) { Query = new SearchQuery(value); dirty = true; } private void RequestFinishEditing() { blockThroughFrame = Time.frameCount + 1; finishEditingRequested = true; } private void ApplyPendingFocusRelease() { if (!finishEditingRequested || (Object)(object)input == (Object)null) { return; } EventSystem current = EventSystem.current; if (!((Object)(object)current != (Object)null) || !current.alreadySelecting) { blockThroughFrame = Time.frameCount + 1; if ((Object)(object)input != (Object)null && input.isFocused) { input.DeactivateInputField(false); } if ((Object)(object)current != (Object)null && (Object)(object)current.currentSelectedGameObject == (Object)(object)((Component)input).gameObject) { current.SetSelectedGameObject((GameObject)null); } finishEditingRequested = false; } } internal void Clear() { if (!((Object)(object)input == (Object)null)) { if (input.isFocused) { RequestFinishEditing(); } input.SetTextWithoutNotify(""); Query = new SearchQuery(""); dirty = false; } } private void LateUpdate() { ApplyPendingFocusRelease(); if ((Object)(object)gui == (Object)null || (Object)(object)input == (Object)null || (Object)(object)root == (Object)null || !InventoryGui.IsVisible() || !root.activeInHierarchy) { return; } UpdateLanguage(); PositionField(); bool flag = (float)CraftTimer.GetValue(gui) >= 0f; if (((Selectable)input).interactable == flag) { ((Selectable)input).interactable = !flag; } if (!dirty || flag || (Object)(object)Player.m_localPlayer == (Object)null) { return; } dirty = false; try { Refresh.Invoke(gui, new object[1] { false }); if ((Object)(object)gui.m_recipeListScroll != (Object)null) { gui.m_recipeListScroll.value = 1f; } } catch (Exception error) { Plugin.Report(error); initializationFailed = true; ((Behaviour)this).enabled = false; Clear(); root.SetActive(false); if ((Object)(object)Current == (Object)(object)this) { Current = null; } } } private void OnDestroy() { if ((Object)(object)Current == (Object)(object)this) { Current = null; } if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } } } internal sealed class SearchQuery { private static readonly Regex Tags = new Regex("<[^>]*>", RegexOptions.Compiled); private readonly string[] words; internal bool IsEmpty => words.Length == 0; internal SearchQuery(string value) { words = Normalize(value).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); } internal bool Matches(string itemName) { if (IsEmpty) { return true; } string text = Normalize(Tags.Replace(itemName ?? string.Empty, string.Empty)); string[] array = words; foreach (string value in array) { if (text.IndexOf(value, StringComparison.Ordinal) < 0) { return false; } } return true; } private static string Normalize(string value) { return (value ?? string.Empty).Normalize(NormalizationForm.FormKC).ToUpperInvariant().Replace('Ё', 'Е'); } }