Please disclose if your mod was created primarily 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 ValheimPrefabParser v2.0.0
BepInEx/plugins/Valheimprefabparser.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Valheimprefabparser")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Valheimprefabparser")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("3b0a1528-adb0-4755-9b2f-2d2b5d70ab58")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimPrefabParser; [BepInPlugin("com.yourname.valheimprefabparser", "Valheim Prefab Parser", "2.0.0")] public class PrefabParserPlugin : BaseUnityPlugin { [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ZNetScene_Awake_Patch { private static void Postfix() { Log.LogInfo((object)"ZNetScene.Awake finished — waiting for ObjectDB..."); if (!_parsingStarted) { ((MonoBehaviour)_instance).StartCoroutine(_instance.WaitForObjectDBAndParse()); } } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] public static class ObjectDB_CopyOtherDB_Patch { private static void Postfix() { Log.LogInfo((object)"ObjectDB.CopyOtherDB finished."); if (!_parsingStarted && (Object)(object)ZNetScene.instance != (Object)null) { Log.LogInfo((object)"ZNetScene is ready — starting parse from CopyOtherDB."); _parsingStarted = true; TriggerParsing(); } } } [CompilerGenerated] private sealed class <ParseCoroutine>d__22 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private List<GameObject> <prefabs>5__2; private Dictionary<string, List<GameObject>> <categories>5__3; private int <processed>5__4; private List<GameObject>.Enumerator <>7__wrap4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ParseCoroutine>d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } <prefabs>5__2 = null; <categories>5__3 = null; <>7__wrap4 = default(List<GameObject>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; Log.LogInfo((object)"Starting parse (coroutine mode)..."); <prefabs>5__2 = GetAllPrefabs(); <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; <categories>5__3 = new Dictionary<string, List<GameObject>>(); <processed>5__4 = 0; <>7__wrap4 = <prefabs>5__2.GetEnumerator(); <>1__state = -3; break; case 2: <>1__state = -3; break; } while (<>7__wrap4.MoveNext()) { GameObject current = <>7__wrap4.Current; if (!((Object)(object)current == (Object)null)) { string key = DeterminePrefabCategory(current); if (!<categories>5__3.TryGetValue(key, out var value)) { value = new List<GameObject>(); <categories>5__3[key] = value; } value.Add(current); if (++<processed>5__4 % 50 == 0) { Log.LogInfo((object)$"Categorized: {<processed>5__4}/{<prefabs>5__2.Count}"); <>2__current = null; <>1__state = 2; return true; } } } <>m__Finally1(); <>7__wrap4 = default(List<GameObject>.Enumerator); WriteFile(BuildContent(<prefabs>5__2.Count, <categories>5__3)); Log.LogInfo((object)$"Done! Total prefabs found: {<prefabs>5__2.Count}"); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap4).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <WaitForObjectDBAndParse>d__15 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private float <elapsed>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForObjectDBAndParse>d__15(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <elapsed>5__2 = 0f; break; case 1: <>1__state = -1; break; } if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items.Count == 0) { <elapsed>5__2 += Time.deltaTime; if (!(<elapsed>5__2 > 30f)) { <>2__current = null; <>1__state = 1; return true; } Log.LogWarning((object)"Timeout: ObjectDB was not ready within 30 seconds. Starting parse without it."); } if (_parsingStarted) { return false; } _parsingStarted = true; Log.LogInfo((object)$"ObjectDB ready ({ObjectDB.instance?.m_items.Count ?? 0} items) — starting parse."); TriggerParsing(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGUID = "com.yourname.valheimprefabparser"; public const string PluginName = "Valheim Prefab Parser"; public const string PluginVersion = "2.0.0"; internal static ManualLogSource Log; private static PrefabParserPlugin _instance; private Harmony _harmony; internal static ConfigEntry<bool> UseCoroutine; internal static ConfigEntry<string> OutputFileName; internal static ConfigEntry<bool> IncludeComponentList; private static bool _parsingStarted; private void Awake() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown _instance = this; Log = ((BaseUnityPlugin)this).Logger; UseCoroutine = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseCoroutine", true, "Spread parsing across frames to avoid a freeze on load."); OutputFileName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "OutputFileName", "valheim_prefabs.txt", "Output file name. Created next to the plugin .dll."); IncludeComponentList = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeComponentList", false, "Print every Unity component on each prefab. Makes the file large."); _harmony = new Harmony("com.yourname.valheimprefabparser"); _harmony.PatchAll(); Log.LogInfo((object)"Valheim Prefab Parser v2.0.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void TriggerParsing() { if (!((Object)(object)_instance == (Object)null)) { if (UseCoroutine.Value) { ((MonoBehaviour)_instance).StartCoroutine(_instance.ParseCoroutine()); } else { ParseSync(); } } } [IteratorStateMachine(typeof(<WaitForObjectDBAndParse>d__15))] private IEnumerator WaitForObjectDBAndParse() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForObjectDBAndParse>d__15(0); } private static List<GameObject> GetAllPrefabs() { //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) HashSet<int> seen = new HashSet<int>(); List<GameObject> prefabs = new List<GameObject>(); if ((Object)(object)ObjectDB.instance != (Object)null) { Log.LogInfo((object)" Reading ObjectDB.m_items..."); foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item != (Object)null) { TryAdd(item.gameObject); } } Log.LogInfo((object)" Reading ObjectDB.m_recipes..."); foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if ((Object)(object)recipe?.m_item != (Object)null) { TryAdd(((Component)recipe.m_item).gameObject); } } Log.LogInfo((object)$" Items: {ObjectDB.instance.m_items.Count}, Recipes: {ObjectDB.instance.m_recipes.Count}"); } else { Log.LogWarning((object)"ObjectDB.instance is null — item data will be incomplete."); } if ((Object)(object)ZNetScene.instance != (Object)null) { Log.LogInfo((object)" Reading ZNetScene.m_prefabs..."); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { TryAdd(prefab); } Log.LogInfo((object)$" ZNetScene prefabs: {ZNetScene.instance.m_prefabs.Count}"); } else { Log.LogWarning((object)"ZNetScene.instance is null — scene data will be incomplete."); } Log.LogInfo((object)" Scanning Resources.FindObjectsOfTypeAll..."); GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>(); foreach (GameObject val in array) { Scene scene = val.scene; if (string.IsNullOrEmpty(((Scene)(ref scene)).name)) { TryAdd(val); } } Log.LogInfo((object)$" Total unique prefabs collected: {prefabs.Count}"); return prefabs; void TryAdd(GameObject go) { if (!((Object)(object)go == (Object)null) && seen.Add(((Object)go).GetInstanceID())) { prefabs.Add(go); } } } private static string DeterminePrefabCategory(GameObject go) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected I4, but got Unknown ItemDrop component = go.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { ItemType itemType = component.m_itemData.m_shared.m_itemType; switch (itemType - 1) { case 2: case 3: case 13: case 21: return "01_Weapons"; case 4: return "02_Shields"; case 5: case 6: case 10: case 11: case 16: return "03_Armor"; case 8: case 22: return "04_Ammunition"; case 1: if (!(component.m_itemData.m_shared.m_food > 0f)) { return "06_Consumables"; } return "05_Food"; case 0: return "07_Materials"; case 12: return "08_Trophies"; case 18: return "09_Tools"; case 14: return "10_Torches"; case 17: return "11_Utility"; default: return "12_Other_Items"; } } if ((Object)(object)go.GetComponent<Piece>() != (Object)null) { return "13_Buildings"; } Character component2 = go.GetComponent<Character>(); if ((Object)(object)component2 != (Object)null) { if (component2.m_boss) { return "14_Bosses"; } if (((Object)go).name.Contains("Player")) { return "15_Player"; } if ((Object)(object)go.GetComponent<Humanoid>() != (Object)null) { return "16_Humanoids"; } if ((Object)(object)go.GetComponent<MonsterAI>() != (Object)null) { return "17_Monsters"; } if ((Object)(object)go.GetComponent<Tameable>() != (Object)null) { return "18_Tameable"; } return "19_Creatures"; } if ((Object)(object)go.GetComponent<Plant>() != (Object)null) { return "20_Plants"; } if ((Object)(object)go.GetComponent<TreeBase>() != (Object)null) { return "21_Trees"; } if ((Object)(object)go.GetComponent<MineRock>() != (Object)null) { return "22_Minerals"; } if ((Object)(object)go.GetComponent<MineRock5>() != (Object)null) { return "22_Minerals"; } if ((Object)(object)go.GetComponent<Container>() != (Object)null) { return "23_Containers"; } if ((Object)(object)go.GetComponent<CraftingStation>() != (Object)null) { return "24_Crafting_Stations"; } if ((Object)(object)go.GetComponent<Fireplace>() != (Object)null) { return "25_Fireplaces"; } if ((Object)(object)go.GetComponent<TeleportWorld>() != (Object)null) { return "26_Portals"; } if ((Object)(object)go.GetComponent<Pickable>() != (Object)null) { return "27_Pickables"; } if ((Object)(object)go.GetComponent<SpawnArea>() != (Object)null) { return "28_Spawners"; } if ((Object)(object)go.GetComponent<Ship>() != (Object)null) { return "29_Ships"; } if ((Object)(object)go.GetComponent<Projectile>() != (Object)null) { return "30_Projectiles"; } if ((Object)(object)go.GetComponent<ParticleSystem>() != (Object)null) { return "31_VFX_Effects"; } if ((Object)(object)go.GetComponent<AudioSource>() != (Object)null && (Object)(object)go.GetComponent<ZNetView>() == (Object)null) { return "32_SFX"; } string name = ((Object)go).name; if (name.StartsWith("vfx_") || name.StartsWith("sfx_") || name.StartsWith("fx_")) { return "31_VFX_Effects"; } return "99_Other"; } private static Dictionary<string, List<GameObject>> CategorizePrefabs(List<GameObject> prefabs) { Dictionary<string, List<GameObject>> dictionary = new Dictionary<string, List<GameObject>>(); foreach (GameObject prefab in prefabs) { if (!((Object)(object)prefab == (Object)null)) { string key = DeterminePrefabCategory(prefab); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<GameObject>()); } value.Add(prefab); } } return dictionary; } private static string BuildContent(int totalCount, Dictionary<string, List<GameObject>> categories) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("╔══════════════════════════════════════════════════════════╗"); stringBuilder.AppendLine("║ VALHEIM PREFAB PARSER — FULL LIST ║"); stringBuilder.AppendLine("╚══════════════════════════════════════════════════════════╝"); stringBuilder.AppendLine($" Date: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); stringBuilder.AppendLine($" Total prefabs: {totalCount}"); stringBuilder.AppendLine($" Categories: {categories.Count}"); stringBuilder.AppendLine(); stringBuilder.AppendLine("── SUMMARY ─────────────────────────────────────────────────"); foreach (KeyValuePair<string, List<GameObject>> item in categories.OrderBy((KeyValuePair<string, List<GameObject>> c) => c.Key)) { stringBuilder.AppendLine($" {item.Key,-35} {item.Value.Count,5} pcs."); } stringBuilder.AppendLine(); stringBuilder.AppendLine("── DETAILED LIST ───────────────────────────────────────────"); foreach (KeyValuePair<string, List<GameObject>> item2 in categories.OrderBy((KeyValuePair<string, List<GameObject>> c) => c.Key)) { stringBuilder.AppendLine(); stringBuilder.AppendLine($"┌─ {item2.Key} ({item2.Value.Count})"); foreach (GameObject item3 in item2.Value.OrderBy((GameObject g) => ((Object)g).name)) { stringBuilder.AppendLine("│ " + ((Object)item3).name); if (IncludeComponentList.Value) { IOrderedEnumerable<string> values = from s in (from c in item3.GetComponents<Component>() where (Object)(object)c != (Object)null select ((object)c).GetType().Name).Distinct() orderby s select s; stringBuilder.AppendLine("│ [" + string.Join(", ", values) + "]"); } } stringBuilder.AppendLine("└───────────────────────────────────────────────────────────"); } return stringBuilder.ToString(); } private static void WriteFile(string content) { try { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), OutputFileName.Value); File.WriteAllText(text, content, Encoding.UTF8); Log.LogInfo((object)("File written: " + text)); } catch (Exception ex) { Log.LogError((object)("Failed to write file: " + ex.Message)); } } private static void ParseSync() { try { Log.LogInfo((object)"Starting parse (synchronous mode)..."); List<GameObject> allPrefabs = GetAllPrefabs(); Dictionary<string, List<GameObject>> categories = CategorizePrefabs(allPrefabs); WriteFile(BuildContent(allPrefabs.Count, categories)); Log.LogInfo((object)$"Done! Total prefabs found: {allPrefabs.Count}"); } catch (Exception arg) { Log.LogError((object)$"Parse failed: {arg}"); } } [IteratorStateMachine(typeof(<ParseCoroutine>d__22))] private IEnumerator ParseCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ParseCoroutine>d__22(0); } }