"If it's worth doing, it's worth overdoing!"
Current version: 1.0.0
Pinsanity finds nearby resources and pins them on the Valheim minimap. Food, wood, metals, dungeon entrances, spawners, Dvergr bases, valuables inside buried treasure chests ... even the amount of honey contained in player-built beehives ... over 160 different things are tracked, configurable (on or off), and labeled however players prefer. Includes a GUI to make it all super-easy.
On top of a host of things players care about...
- Showing "the actual icon" for each resource
- Loading icons directly from the game's assets, instead of fiddling with folders and image files
- Incredible configurability (turning things on/off, customizing labels)
- Wrapped in a GUI for maximum convenience
- Various performance optimizations
- (See more, including screenshots, on the Nexus Mods page)
...there's a verbose pile of things that I didn't mention elsewhere.
Some things that you, the skeptical coder or could-be contributor, might be interested to know:
- Finds every type of deposit, including the ones that shatter into fragments
- Helps players find every last fragment of copper, silver, etc.
- Each fragment pin is placed at the visual center of the mesh for perfect accuracy
- Automatically detects mined-out fragments and removes their pins
- Shows treasure chests with specific items (silver necklaces, onion seeds, etc).
- Empty treasure chests show an "EMPTY" label to save time
- Player-made beehives show how much honey is available for picking (if empty, pins do not display)
- GUI window is resizable and persists between sessions, so the layout stays how players left it
- Various pains have been taken to ensure "things you type" don't get lost without being saved (label edits are always saved when players leave a field or close the window)
- Numerous forms of input are suppressed while the settings window is open, so players won't accidentally declare war on a Dvergr while tweaking settings
- Players can close the GUI window via their own configured hotkey or ESC (and not accidentally pause the game at the same time)
- After they're done making changes, the scanner will fire immediately (rather than waiting a full interval)
- Numerous gameplay inputs and minimap interactions are suppressed (along with camera rotation and cursor recapture) while the window is open, to prevent unintended in-game actions while the player is adjusting settings
- All suppression flags reset on each open/close cycle, ensuring suppression state never carries over between sessions
- GUI window opening is blocked if not in-game, the ESC menu is open, or the player object isn't fully loaded
_weDidPauseprevents unpausing a game that was already paused by a third-party mod before the window opened- Int fields are buffered during typing, preventing half-entered values from being committed prematurely
- Int fields are also flushed on window close, since focus-loss doesn't reliably fire in the same frame as a button click
_isRebuildPendingdefers scanner rebuild until window close rather than triggering on every individual toggle, avoiding redundant restarts during multi-setting changes
- Different object types store their icon data in different places, so five separate lookup strategies are used to find the right icon for each resource (
Pickablecomponent,PickableItemcomponent,DropOnDestroyedcomponent, ZNetScene/ObjectDB name lookup, or generic map icon (dot, house, etc.)) - ZNetScene is tried before ObjectDB for speed since it's a scene-level lookup rather than a search across the full item database
- A clean-name fallback strips
Pickable_prefixes and(Clone)suffixes, recovering icon matches that would otherwise fail due to naming inconsistencies
- Flametal spires are re-validated against ZNetView on every scan to filter out phantom LOD meshes with no network identity, preventing false pins; a
transform.positionfallback also handles renderers not yet fully initialized - A three-stage name lookup ensures dungeon variants (like
Crypt1(Clone)andCrypt4(Clone)) are found automatically, including potential new variants added by future game updates (Crypt5(Clone), etc).
- Filtering discards up to 90% of scene objects before expensive lookup operations to minimize CPU cost
- Scan collections are pre-allocated and reused every pass to avoid per-scan GC pressure
- A reentrancy guard (
_isScanning) prevents a slow scan from overlapping the next one - Scanning is skipped entirely when the game is paused or the settings window is open
unscaledDeltaTimedrives the scan timer so scan interval is consistent regardless of game speed
- Numerous object types and icons are cached, eliminating redundant lookups and hierarchy traversals on subsequent scans
- All caches are invalidated on scene load, preventing stale references from causing incorrect behaviour or crashes after a world change
- Dungeon loot icons are never cached since objects with identical names (ie.
Pickable_ForestCryptRandom) can yield different items - Two collections of resource pins (a dictionary for fast lookup and a list for iteration) are kept in sync, with snapshot copies on read to prevent exceptions when pins are removed mid-scan
- Default pin sprites are captured once from the first pin created and reused when a custom icon is no longer needed, ensuring pins always have a valid fallback appearance
- All scan steps wrapped in
try/finallyso_isScanningis always cleared even on exception - GUI exceptions trigger a force-close with up to 3 auto-reopen attempts (deferred one frame to let IMGUI settle); a permanent error overlay is shown after repeated failures, making persistent problems visible rather than silent
- Reflection access to Valheim's private
m_pickedfield fails gracefully with a one-time warning, so a game update that renames the field produces a visible notice rather than silently pinning already-picked objects
- To reduce future maintenance overhead, config setters are generated via reflection for
GeneralConfigproperties, so adding a new setting requires no parser changes - Per-property bounds and defaults are stored in a lookup table to keep the reflection loop generic
DebugLoggerchecks debug state internally so callers never need to writeif (IsDebug)themselves- Three logging tiers: Debug (state changes), Trace (per-object firehose), and Benchmark
- Benchmark mode logs a 14-bucket per-scan timing table to nanosecond precision, including a separate measurement of
Minimap.AddPin()overhead - Suppression-once logic avoids per-frame log spam while still providing diagnostic detail when needed
- Custom labels with special characters (like
#) won't break the mod - Future updates (ie. Deep North) won't break player customizations: missing or invalid settings are detected on load and the file is resaved automatically with corrected values
Oh, and the whole thing is "well-commented."
Did I overdo it? Yes, I did.
Sorry, make that "obsessively commented."
View the roadmap of future updates here.
- Install BepInEx if you haven't already.
- Download the latest DLL from the Releases page.
- Drop it into your BepInEx plugins folder:
\SteamLibrary\steamapps\common\Valheim\BepInEx\plugins - Launch Valheim. The mod creates a config file automatically on first run with sensible defaults.
- Press F8 in-game to open the settings window. (The hotkey is configurable in the config file.)
Pinsanity can be configured in two ways: the in-game settings window (recommended) and the config file (for anything the window doesn't cover, like the hotkey).
The in-game window lets players toggle resources on or off, customize labels, switch entire categories on or off, adjust scan radius and interval, and enable various debug modes: all live, with changes saving instantly.
A config file is created automatically at \SteamLibrary\steamapps\common\Valheim\BepInEx\config\FoobyScroobs.Pinsanity.cfg.
Players who prefer to manually edit config files will see organized sections with headers, inline comments, and notes for certain resources, instead of the usual wall of auto-generated BepInEx boilerplate.
General settings:
| Setting | Default | Range | Description |
|---|---|---|---|
| Enabled | true | true/false | Master on/off switch for the entire mod |
| ShowLabels | true | true/false | Show or hide all pin labels globally |
| ScanRadius | 100 | 1–500 | How far from the player to scan, in meters |
| ScanInterval | 5000 | 500–30000 | How often to scan, in milliseconds |
| Hotkey | F8 | any KeyCode | Key to open/close the settings window |
Each resource also has two config entries: Enable{Name} (true/false) and {Name}Label (display text). These are all managed automatically by the in-game window, but can be hand-edited if preferred.
Pinsanity uses its own hand-rolled config format and its own in-game settings UI. This may not be compatible with the BepInEx ConfigurationManager mod.
Honest confession: I built the entire in-game settings window before I even knew BepInEx ConfigurationManager existed. In hindsight, that might have saved a few weeks and a few hundred lines of IMGUI code. On the other hand, the settings window is pretty awesome, so I choose not to think about it.
No known conflicts at this time.
All patches are prefixes that only fire while the settings window is open. The game is paused during this time, so even if another mod patches the same methods, conflicts are unlikely to affect normal gameplay.
The patched methods are:
- GameCamera.LateUpdate
- GameCamera.UpdateMouseCapture
- ZInput.GetButtonDown
- ZInput.GetKeyDown
- Minimap.Update
- Minimap.OnMapLeftClick
- Minimap.OnMapRightClick
- Minimap.OnMapDblClick
- Minimap.SelectIcon
- Minimap.ToggleIconFilter
If Valheim updates and something breaks, check the GitHub Issues page.
Not tested in multiplayer -- wanna check it and let me know?
Periodically, Pinsanity scans all active GameObjects within the configured radius, discards all the ones that aren't immediately interesting, matches the remaining candidates against a list of known resource definitions, and places minimap pins for everything the player wants to see. Pins are removed automatically when resources go out of range, are picked up, or are mined out.
Fragment-based ore deposits (copper, silver, etc.) are handled specially: Valheim replaces the original deposit with up to 130 individual fragment meshes when first struck, and Pinsanity tracks each fragment independently so players can see exactly what's left underground.
The scan runs on a configurable timer (default: every 5 seconds).
None currently! If you encounter problems:
- Set
IsDebug = truein the config file (and optionallyIsTrace = truefor extreme detail). - Reproduce the issue.
- Find your log file at
\SteamLibrary\steamapps\common\Valheim\BepInEx\LogOutput.log - Open a GitHub Issue and attach the log.
- Bug reports & feature requests: GitHub Issues -- response times may be slow, this is a hobby!
- Feedback & comments: Nexus Mods
- Creator: FoobyScroobs / GitHub
Pull requests are accepted case-by-case: please open an issue first to discuss.
Maintenance: Gaming is an occasional hobby. This mod does what I need. If you want to extend it, fork away!
This mod is a labor of love, painstakingly built over the course of 10 months.
It was built from scratch by analyzing the game's decompiled code, not by copying or modifying existing mods.
I definitely used AI assistance (Claude) to create this mod -- and I definitely did not do it with a single prompt.
Pinsanity was built iteratively, tested, debugged, audited against a 17-point checklist, and then in true OCD fashion: it was audited again.
Every line of code has been read, understood(ish 😉), and approved by a human.
No memory leaks. No thread safety issues. No lifecycle problems. No hot-path concerns.
That said, I am not a career programmer, and while everything in this mod makes sense to me, it might not make sense to someone who's been doing it for 20 years.
Audit it yourself: it's all here.
See CHANGELOG.md.
MIT License: see LICENSE
Free to use, modify, and distribute.