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.
The Eye
A deterministic, cross-platform Valheim API extraction tool, vibe coders love it!
| Date uploaded | 2 weeks ago |
| Version | 2.3.2 |
| Download link | Wubarrk-The_Eye-2.3.2.zip |
| Downloads | 29 |
| Dependency string | Wubarrk-The_Eye-2.3.2 |
This mod requires the following mods to function
denikson-BepInExPack_Valheim
BepInEx pack for Valheim. Preconfigured with the correct entry point for mods and preferred defaults for the community.
Preferred version: 5.4.2350ValheimModding-JsonDotNET
Shared version 13.0.3 of Json.NET from Newtonsoft, net45 package for use in Valheim mods. Maintained by the ValheimModding team.
Preferred version: 13.0.4README
ποΈ WubarrksEye (TheEye)
"Stop guessing what the IL does. Stop decompiling 130,000 lines just to find out if
CanSenseTargetis virtual or static. The truth is in the assembly, and now it's in your hands."
WubarrksEye (TheEye) is a deterministic, cross-platform Valheim extraction tool. It is, without exaggeration, a vibe coder's wet dream. But let's be real, with this data at your fingertips you're not just a vibe coder anymore. You're an AI-augmented game mod designer, and your dreams can come true. When Valheim 1.0 drops and the API shifts under everyone's feet, this is the tool that maps the new world, tells you exactly what changed against your pre-1.0 baseline, and feeds it straight to your AI pair programmer.
Forget hand-grepping decompiled source for hours. Forget guessing why your Harmony patches misfire because you bound the wrong overload, or realising months later that your custom monster AI was inert because vanilla's Character.OnDamaged never fires for most creatures. TheEye gives you the ground truth of the game's architecture, serialized data, method call graphs and live world state, straight from memory.
This isn't a dumper. It's an autopsy table for the game engine, and as of 2.3.0 it comes with a before/after report and a mod impact report.
βοΈ What's new in 2.3.0 (the 1.0 migration release)
Built against the 1.0 public-test build (0.221.13, network version 37) and verified headless on both the last pre-1.0 server and the playtest server.
π‘οΈ Survives the build change
0.221.13 moved sectors from Vector2i to Vector2s, replaced the seven ZDOExtraData.Get*s(ZDOID) readers with one GetData(out ...), and turned World.m_worldVersion into an enum. Every member TheEye touches that moved is now late-bound: it is looked up by name at first use and a missing one costs one field in one record, never a whole extractor. Manifest.json gains a Compatibility section listing every game member TheEye relies on and whether this build has it, and the BepInEx log says the same at startup.
π§© ModImpact_Dump.json β which of your mods break on this build
Every plugin BepInEx loaded, plus any folder you point it at ([ModImpact] ScanPluginDirs or dump_eye scan=<dir>), is read with Mono.Cecil and every reference it makes into a game assembly is resolved against the running build. Per plugin: the exact unresolved methods, fields and types, the plugin methods that use them, every [HarmonyPatch] target that is missing or now ambiguous (a new overload appeared), and whether a preloader patcher such as Valheim10Compatibility bridged the gap in memory. Then the live Harmony patch map: who patches which game method. Run it from a clean profile with scan= pointing at your mod-manager profile's plugins folder and you have the day-one breakage list without loading a single mod. What it cannot see: targets found by string name or TargetMethod() show as Unchecked, and a field whose type changed is not bridgeable by any preloader, only by the mod (on 1.0 that is VisEquipment's item fields and the ZoneSystem / ZDOMan sector containers).
𧬠IL fingerprints: Huginn now sees body changes
Each API type carries a MethodBodies table, a hash of every method's normalized IL (opcodes, resolved operand names, relative branch targets). A method whose signature did not move but whose code did now shows up as BodyChanged. That is most of what a game update is (392 of the changes between 0.221.12 and 0.221.13), and it is what transpilers and behaviour-dependent patches trip over.
π¦ββ¬ Huginn's Report, classified
CHANGED API records carry a Members list that names each change the way an assembly differ would: Added, Removed, SignatureChanged, BodyChanged, TypeOnlyRipple (only Vector2i/Vector2s or Int16/Int32 moved) and Renamed (same IL fingerprint under a new name). Huginn_Summary.json gets a Breaking block (types and members removed, signature changes, ripples, RPCs and ZDOVars and console commands removed, plugins broken).
π Huginn_Report.md
The report a person reads first: both builds identified, per-file counts, breaking changes grouped by type, body-only changes, additions, registry changes by source, new items and prefabs, and the mod impact table with the unresolved references ranked by how many plugins need them (the bridge priority list).
π§ Schema-aware diff
Upgrading TheEye between two dumps no longer turns every record CHANGED. A top-level field that exists in only one side's schema is excluded from the comparison and listed once per file as SchemaAdded / SchemaRemoved. Your 2.2.0 baseline stays useful.
π ZDO keys resolve
Every string literal in the game and plugin assemblies is hashed (plus the _u / _i pair suffixes ZDO.GetHashZDOID builds) into the ZDO key reverse map. Most of the #hash keys in a modded dump now have names. New SectorIndex field on 0.221.13+.
ποΈ Console
dump_eye scan=<dir> noimpact, plus the 2.2.0 options. The active-area override finds InActiveArea / OutsideActiveArea overloads by name, so it works on both sides of the Vector2s change.
βοΈ What's new in 2.2.0 (the pre-1.0 baseline release)
π§Ύ Manifest.json β every dump knows what build it came from
Valheim stamps every DLL 0.0.0.0. So TheEye now writes, before anything else, the game version string with build hash, the Steam buildid, the md5 and size of every DLL in Managed/, the BepInEx version and every loaded plugin, the world name, seed and global keys, and the options used. A dump from a contaminated profile is visible on sight.
π¦ββ¬ Huginn's Report β a real diff, not a line hash
Every record in every file is keyed on its identity (type name, prefab name, ZDO UID, _Source/_Key). Huginn now says, per file, exactly which records were ADDED, REMOVED and CHANGED, and for CHANGED lists the field-level deltas with old and new values. Huginn_Summary.json has the counts. Pick the baseline: dump_eye baseline=<dir> or the config. Run it after 1.0 against your pre-1.0 dump and read the summary first.
πΊοΈ API dump rewritten
Full member surface per type: fields with modifiers and constant values, properties with accessor visibility, events, constructors, full method signatures with virtual / override / abstract, interfaces, attributes, nested types, enum values. Members are keyed by name so a renamed overload is one line in the diff. Game assemblies only by default; the 20,000-type mscorlib noise is gone.
π Registry_Dump.json β where a new biome shows up first
Enums with values, the ZDOVars hash table, console commands with cheat/server/admin flags, spawn tables, creature spawners, environments and biome weather, raids, piece tables with every piece and its costs, skill definitions, and the full localization table.
𧬠ZDO dump that means something
Prefab hash resolved to a name, rotation, sector, persistence, revisions, creation time, and every key/value the ZDO carries with hashes mapped back to ZDOVars names (health, owner, items...). The session-long ZNetScene active-area override that forced every object in the world to instantiate is now off by default.
π‘ RPC names in the call graph
Every ZNetView / ZRoutedRpc / ZRpc.Register string literal, and the method that registers it. A renamed RPC breaks every mod that Invoke()s it by name; now it is one diff line.
π₯οΈ Headless
A dedicated server has no console to type into. [Automation] AutoDumpOnWorldLoad = true dumps once the world is populated, and AutoDumpQuit exits afterwards. One-shot server baseline.
π§Ή Determinism and hygiene
UTF-8 without BOM (line splitters were dropping the first record). Types, prefabs, items, ZDOs and indexes sorted. Invariant-culture round-trip floats. Two dumps of the same build on the same world are byte-identical, and that was verified.
βοΈ Features carried forward
πΈοΈ Deep Call-Graph Extraction
Walks the method IL and emits call and field-access edges. Does an override call base? Which overload does a call site really reach, static or instance? Who calls method X? All in CallGraph_Dump.json, with call vs callvirt on every edge and a CalledBy inverted index.
π Serialized Value Extraction
Declarations tell you a field exists; Values_Dump.json tells you what is in it. Every item, recipe, status effect, prefab component, vegetation and location entry, flattened to dotted paths, cycle-safe, with per-entry budgets so it never explodes.
π Zero-Stall Background Architecture
Coroutine-batched, RAM-profile scaled, cancellable, streamed straight to disk. The game keeps rendering, the server keeps ticking. A full dump on a dedicated server takes about a minute.
π Guide to Extraction
- Launch the Game and load a world. (API and call graph work from the main menu; everything else needs a world.)
- Run the Dump:
dump_eyein the F5 console, or F8 β Start Dump.eye_statusshows progress. F9 toggles the HUD. - Options:
dump_eye baseline=<dir> nodiff noml nozdo noscene noloc - Review the Goods:
BepInEx/config/WubarrksEye_Dumps/<timestamp>/.AgentML_Complete_Dump.zipbundles everything as one minified JSONL plus the manifest for machine ingestion. - Write Better Mods, you vibe coder you.
What You Will Find Inside
| File | Contains | Keyed on |
|---|---|---|
Manifest.json |
Build identity, md5 table, plugins, world, options, per-file results | |
API_Dump.json |
Every type in the game assemblies with its full member surface | FullName |
Values_Dump.json |
Field contents for items, recipes, status effects, prefabs, vegetation, locations | _Source/_Key |
Registry_Dump.json |
Enums, ZDOVars, commands, spawns, environments, raids, pieces, skills, localization | _Source/_Key |
CallGraph_Dump.json |
Per-method IL edges, CalledBy index, RpcRegistration index |
Type.Signature |
Prefabs_Dump.json / ObjectDB_Dump.json / Scene_Dump.json |
Component lists | Name |
ZDO_Dump.json |
Every ZDO in the loaded world with its key/values | UID |
Huginn_Diff_Report.jsonl + Huginn_Summary.json |
ADDED / REMOVED / CHANGED against the baseline |
π Why Before You Grep The Decompile
API_Dump.json answers three questions without reading a line of decompiled source:
- Which type declares a member. Members are
DeclaredOnly, so ifm_targetCreaturesits underMonsterAIit is not onBaseAI. Binding the wrong type throwsMissingFieldExceptioninto acatchthat swallows it. - The full overload set with complete parameter types.
virtual/override/abstract, derived fromGetBaseDefinition().
CallGraph_Dump.json answers method body questions reflection cannot see:
ChainsToBase:Humanoid.OnDamageddoes not call base, so a patch onCharacter.OnDamagednever fires for most creatures.Player.OnDamageddoes.Calls[].Op:callvscallvirt, which is how you tell abasechain from re-dispatch, and a static target from an instance one.CalledByandRpcRegistrationindexes.ModImpact_Dump.json: one record per plugin withUnresolved,Bridged,PatchTargets.ProblemsandStatus, thenHarmonyPatchindex records and aModImpactSummary.Huginn_Report.md,Huginn_Summary.json,Huginn_Diff_Report.jsonlwhen a baseline exists.
Accessibility is the runtime assembly's. Projects compiling against a publicized assembly_valheim see private members here as directly callable.
π§ Taking a baseline before Valheim 1.0
- Copy the game folder first. Steam overwrites it on launch day and the old build is gone.
- Dump on a clean profile (BepInEx + TheEye only).
Manifest.jsonlists every plugin, so contamination is at least visible. - Dump from the main menu, from inside a world, and from your dedicated server if you patch server code.
- After the update:
dump_eye baseline=<your pre-1.0 dump> scan=<your profile's BepInEx/plugins>and readHuginn_Report.mdfirst. The mod impact table at the bottom is the list of mods to fix or bridge; theBreakingblock inHuginn_Summary.jsonis the count.
βοΈ Configuration (wubarrk.theeye.cfg)
| Key | Default | Purpose |
|---|---|---|
[API] ApiAssemblyFilter |
assembly_*,gui_framework,Splatform |
Assemblies covered by the API and enum dumps. * for everything loaded |
[API] ApiIlFingerprints |
true |
MethodBodies IL hashes per type (BodyChanged detection) |
[ModImpact] ScanPluginDirs |
empty | Extra plugin folders to scan statically (semicolon separated) |
[Huginn] HuginnMarkdownReport |
true |
Write Huginn_Report.md |
[ZDO] ZdoIncludeData |
true |
Emit every ZDO key/value |
[ZDO] ForceGlobalActiveArea |
false |
The old always-on instantiate-everything patch. Leave off |
[Registry] DumpLocalization |
true |
Full token β text table |
[Huginn] HuginnBaselineDir |
empty | Fixed baseline directory |
[Huginn] HuginnMaxDeltasPerRecord |
200 |
Cap on deltas per CHANGED record |
[Automation] AutoDumpOnWorldLoad / AutoDumpDelaySeconds / AutoDumpQuit |
false / 20 / false |
Headless one-shot dump |
[Hardware] SystemMemoryProfile |
Rig_16GB |
Batch sizes per frame |
β οΈ Dependencies: BepInEx, Newtonsoft.Json (installed automatically as the JsonDotNET dependency)
π License
MIT. Copyright (c) 2026 Ross G. Support development at https://www.patreon.com/c/WubarrkDev
CHANGELOG
Changelog β The Eye
2.3.2 β 2026-09-25 (Valheim 1.0.16 compatibility)
Rebuilt against the Valheim 1.0.16 release assemblies (client build 25527674, dedicated server
build 25527701, network version 40) and re-verified: a static reference check against both the
client and dedicated-server assemblies, and a headless boot of the 1.0.16 dedicated server. No
reader was broken - GameShape's BuildShape probe and every other late-bound member are
unchanged from 1.0.15, and the only two members 1.0.16 removed (ObjectDB.GetAllFoodItems(),
ButtonSfx.Start()) are not read by TheEye.
- One comment corrected to stop naming a specific old build as if it were current: the
ZNetScene.InActiveArea/OutsideActiveAreastaticness note inZDOExistencePatch.cs(verified unchanged against the 1.0.16 decompile).
2.3.1 β 2026-09-09 (Valheim 1.0.7 compatibility)
Rebuilt against the Valheim 1.0.7 release assemblies (client build 25185596, dedicated server
build 25185644, network version 39) and BepInEx 5.4.2350, and loaded on the 1.0.7 Linux dedicated
server with no Harmony, missing-member or type-load errors. No reader was broken by 1.0.7 - every
member GameShape late-binds is still present or already had a fallback - but the instrument was
blind to the release-stage sector change. Now it is not.
Manifest Compatibility gains BuildShape
- The 1.0.7 release changed the sector API a second time after the 0.221.13 playtest:
ZDOMan.FindSectorObjects(Vector2s, int area, int distantArea, ...)becameFindSectorObjects(Vector2s, SimulationDistance, ...)andZoneSystem.m_activeArea/m_activeDistantAreawere removed. Nothing in the 2.3.0 probe looked at that shape, so a 2.3.0 dump could not tell the playtest from the release. BuildShaperecords, as{Present, Shape}rows using type names (nevertypeof()on a type an older build may lack):ZDOMan.FindSectorObjects(arity and parameter types), theSimulationDistancestruct,ZoneSystem.m_activeArea/m_activeDistantArea,ZNet.GetSyncedSimulationDistance(),ZoneSystem.SectorIndex,ZoneSystem.GetSectorIndex(Vector3),ZoneSystem.GetZone(Vector3),ZDOMan.SectorToIndex/ZoneSystem.SectorToIndex(moved, not removed), and a one-tokenSectorApiclassification (pre-1.0 / 0.221.13 playtest / 1.0 release).ZDO.GetSectorIndex()now records its return type;ZNetScene.InActiveArea/OutsideActiveArearecord how many overloads are static. AbsentBuildShapemembers are facts about the build, not blind readers: they never count towards the startup MISSING line.- New startup line
[Eye.Compat] Build shape: ..., so a boot log alone answers which build this is.
Unchanged on purpose
- Huginn's
TypeOnlyRipplecollapse still covers onlyVector2i/Vector2sandInt16/Int32. The(int, int) -> SimulationDistancechange is an arity change and keeps surfacing as a real signature change. - Stale comments refreshed (
GameShape,Plugin.ApplyActiveAreaOverride,ZDOExistencePatch,HuginnsReport): the by-name attachment of the active-area prefixes is what survives 1.0 makingZNetScene.InActiveAreastatic.
2.3.0 β 2026-09-08 (the 1.0 migration release)
Built and verified against the Valheim 1.0 public-test build (0.221.13, build 23105022, network version 37) on a headless dedicated server, and against the last pre-1.0 server build.
Survives the build change
ZDO.GetSector()(nowVector2s), the seven removedZDOExtraData.Get*s(ZDOID)readers (nowGetData(out ...)),World.m_worldVersion(now an enum) and theZNetScene.InActiveArea/OutsideActiveAreaoverloads are late-bound throughGameShape. A missing member costs one field, never an extractor. Three strategies for ZDO data (GetData,Get*s, private dictionaries).Manifest.jsonCompatibilitysection: every game member TheEye relies on, present or MISSING, plus the ZDO data strategy in use. Logged at startup.- Manifest
WorldgainsWorldVersionName,ChunkedSave,SaveNumber,FileSource,SaveFiles.
New ModImpact_Dump.json
- Every loaded plugin plus any folder in
[ModImpact] ScanPluginDirs/dump_eye scan=<dir>is read with Mono.Cecil; every member and type reference into a game assembly is resolved against the running build. Unresolved references list the plugin methods that use them. [HarmonyPatch]attribute targets are checked: type-missing, method-missing, overload-missing, and ambiguous (a new overload appeared and no argument types were given).- References missing on disk but present in memory are reported as
Bridged, with theValheim10Compatibility bridgemarker recognised. - Live Harmony patch map (
HarmonyPatchindex records) and aModImpactSummary. - Client-only types are flagged as axis noise when scanning on a server, not counted as broken.
IL fingerprints
API_Dump.jsontypes carryMethodBodies: a hash of each method's normalized IL (opcodes, resolved operand names, relative branch targets, locals, exception handlers).[API] ApiIlFingerprints.
Huginn's Report
- Schema-aware: top-level fields present in only one side are excluded from the comparison and
listed as
SchemaAdded/SchemaRemovedper file. A 2.2.0 baseline diffs cleanly against 2.3.0. - API CHANGED records carry
Members: Added, Removed, SignatureChanged, BodyChanged, TypeOnlyRipple (Vector2i/Vector2s, Int16/Int32 only), Renamed (same IL fingerprint), ValueChanged. Huginn_Summary.jsongainsBreakingcounters and the current dump'sModImpactsummary.- New
Huginn_Report.md: human-readable summary with breaking changes grouped by type, body-only changes, additions, registries by source, content, and the mod impact table with unresolved references ranked by plugin count.[Huginn] HuginnMarkdownReport. ModImpact_Dump.jsonis keyed by plugin GUID / patch target.
ZDO keys
- The key reverse map now hashes every string literal in the game and plugin assemblies, plus the
_u/_ipair suffixes, so most#hashkeys resolve.SectorIndexemitted on 0.221.13+.
Console
dump_eye scan=<dir>andnoimpact.
Reference DLLs
libs-Tools/Mono.Cecil.dll(0.10.4.0, the copy BepInEx 5 loads) added for compile; not shipped.
Known limits (measured 2026-09-08)
- ModImpact cannot see patch targets resolved by string (
AccessTools.Method(typeof(X), "Name")) or byTargetMethod(); they show asUnchecked. Harmony's own log at PatchAll is the backstop. - A field whose type changed (0.221.13:
VisEquipment.m_*Itemstring -> int,ZoneSystem.m_zones,ZDOMan.m_objectsByOutsideSector) cannot be bridged by any preloader; it is reported as unresolved and the only fix is in the mod. Bridged references are recognised by theValheim10Compatibility bridgemarker. Renamedneeds both dumps to carryMethodBodies, so it cannot fire against a 2.2.0 baseline.
2.2.0 β 2026-09-03 (pre-1.0 baseline release)
Provenance
- New
Manifest.json, written before any extractor runs: game version string with build hash, Steam buildid, md5 + size of every Managed DLL, BepInEx version and every loaded plugin, world name/seed/global keys, options used, and per-file results when the dump ends. Also bundled into the AgentML zip.
API dump rewritten
- Full member surface per type: fields (with modifiers and constant values), properties (with
accessor visibility), events, constructors, methods (full signatures with
virtual/override/abstract), interfaces, attributes, nested types, enum values, Unity metadata. - Members are objects keyed by name so Huginn reports member-level adds and removes.
- Only game assemblies by default (
[API] ApiAssemblyFilter); compiler-generated types skipped. - Sequential, sorted by FullName. The old
Parallel.ForEachmade the file order random per run.
Huginn's Report rewritten as a structural diff
- Every record is keyed on its identity; the report says ADDED, REMOVED and CHANGED per file, with
flattened field-level deltas (old/new) for CHANGED and a
Huginn_Summary.jsonof counts. - The baseline can be chosen:
dump_eye baseline=<dir>or[Huginn] HuginnBaselineDir. - The old line-hash diff could not say "removed" and produced an 80 MB report of
ADDED_OR_MODIFIED.
ZDO dump
- Prefab hash resolved to a prefab name. Adds rotation, sector, persistence, object type,
revisions, creation time, and every key/value from
ZDOExtraDatawith hashes reverse-mapped throughZDOVars. Sorted by UID. - The ZNetScene active-area override is now off by default (
[ZDO] ForceGlobalActiveArea). It forced the game to instantiate every ZDO in the world for the whole session and the dump never needed it on a host.
New Registry_Dump.json
- Enums with values,
ZDOVarshash table, console commands with cheat/server/admin flags,SpawnSystemListspawn tables,CreatureSpawnerprefabs, environments and biome environments, random events, piece tables and every piece with requirements, skill definitions, and the full localization table.
Call graph
RpcRegistrationindex records: the RPC name string and the registering method for everyZNetView/ZRoutedRpc/ZRpc.Registercall.- Types and the
CalledByindex are sorted.
Determinism and hygiene
- All output is UTF-8 without BOM (line splitters were dropping the first record).
- Prefabs and ObjectDB items sorted by name; positions and floats in invariant round-trip format.
- Localization now resolves (
Localizationlives inassembly_guiutils; the old reflective lookup askedassembly_valheimand 894 display names stayed as$tokens).
Console and automation
dump_eyeoptions:baseline=<dir> nodiff noml nozdo noscene noloc; results print to the in-game console, not only the BepInEx log. Neweye_status.[Automation] AutoDumpOnWorldLoad/AutoDumpDelaySeconds/AutoDumpQuitfor dedicated servers, which have no console to type into.
2.1.1
- ValueExtractor (
Values_Dump.json) and CallGraphExtractor (CallGraph_Dump.json) added. - Streaming JSON dump engine, AgentML zip bundle, F8/F9 IMGUI controls, line-hash Huginn's Report.
1.2.9
- Fixed Readme file
1.2.8
- Initial public release