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.
SunkenCryptTimer
Client-only HUD countdown + ground rings for dungeons on servers running Venture Location Reset: 'resets in 2d 14h' near any entrance, workbench-style ring at VLR's check radius. No server changes needed.
| Date uploaded | 2 weeks ago |
| Version | 1.1.7 |
| Download link | StandardVibeware-SunkenCryptTimer-1.1.7.zip |
| Downloads | 33 |
| Dependency string | StandardVibeware-SunkenCryptTimer-1.1.7 |
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.2351README
SunkenCryptTimer
A client-only Valheim mod for players on servers that run Venture Location Reset (VLR).
It answers two questions at a glance:
- "When does this dungeon reset?" — a HUD countdown ("Sunken Crypt — resets in 2d 14h") whenever you are near a dungeon entrance: sunken crypts, burial chambers, troll caves, frost caves, goblin camps, dvergr mines, Hildir's dungeons, Ashlands fortresses, and more.
- "How close is too close?" — a workbench-style ground ring around each nearby instanced dungeon at the radius VLR's player-activity check uses for it (45 m for sunken crypts), so you can see the footprint where player-made structures or a tombstone would block the dungeon from ever resetting.
No server changes, no Harmony patches, no world-data writes: the mod only reads the
VV_LastReset stamps VLR leaves on each dungeon's world object — those sync to every
connected client automatically.
Requirements
| Component | Needed? | Notes |
|---|---|---|
| Valheim | on your client | Built/tested against Valheim 1.0.16 (Unity 6 build). Older versions untested. |
| BepInEx | on your client | Tested with denikson-BepInExPack_Valheim 5.4.2351 (BepInEx 5.4.22+). |
| Venture Location Reset | on the server | The mod displays VLR's reset data; without VLR on the server there is simply nothing to show. Any recent 1.x. |
| VLR on your client | optional | If installed (it usually is on modded servers), the countdown uses the server's live reset intervals, synced through Jotunn. If not, set FallbackResetDays to match your server. |
| Jotunn | not required | This mod does not reference Jotunn or any other package. |
The mod does not need to be installed on the server (installing it there is harmless but does nothing — servers have no HUD). Other players don't see your rings or your timer unless they also run the mod.
Installation
With a mod manager (r2modman / Gale / Thunderstore Mod Manager): search for "SunkenCryptTimer" and install. Done.
Manual: copy Plugins/SunkenCryptTimer.dll from the package into your BepInEx
profile's BepInEx/plugins/ folder.
Usage
Timer
Walk near any dungeon VLR manages (default: within 80 m of the entrance). A single line appears at the top-center of the HUD:
Sunken Crypt - resets in 2d 14h— the dungeon still holds its loot.Sunken Crypt - reset ready (regenerates on approach)— the timer has elapsed. VLR only regenerates a dungeon when a player walks within 100 m of it after the timer expires, so it will pop fresh the moment someone (maybe you) shows up.
Timers keep running while the server is empty (the world clock advances 24/7 while the server process runs), and the countdown is computed from live world time — log back in after a week and the label already reflects it.
Zone rings
Each nearby instanced dungeon (crypt, burial chamber, troll cave, ...) gets a ground
ring cloned from the vanilla workbench build-circle: thin rotating dashes hugging the
terrain. Its radius is the distance VLR checks for player activity inside that
dungeon type — 45 m for sunken crypts, computed per type from the game's own dungeon
data (hypot(zoneSize.x/2, zoneSize.z/2), matching VLR's GetDungeonRadius).
Rules of thumb the ring makes visible:
- Build or die inside the ring's area (or within the small entrance zone around the door) and that dungeon stops resetting until the pieces/tombstones are removed.
- A base on the ground outside the ring is safe and never blocks resets.
- Terrain shaping (raising/leveling) never blocks a reset, but VLR reverts terrain within ~12 m of the entrance each time the dungeon resets.
- Rings show while you're within 100 m (VLR's own trigger distance), hide while you are inside the dungeon, and are capped at the 4 nearest features. Ground locations (meadow farms, villages, fuling camps) intentionally get no ring.
Configuration (BepInEx/config/lan124.SunkenCryptTimer.cfg)
All settings are client-side; changes apply immediately (no restart).
| Section | Key | Default | Meaning |
|---|---|---|---|
| General | Enabled | true | Master switch |
| General | DebugLogging | false | Verbose scanner logging |
| HUD | ScanRadiusMeters | 80 | Show the timer within this distance of an entrance |
| HUD | UpdateIntervalSeconds | 1 | Rescan rate |
| HUD | HudOffsetY | 170 | Label offset from the top of the screen (px) |
| Timers | FallbackResetDays | 10 | Assumed interval when VLR's config can't be read — set to your server's actual interval |
| Rings | Enabled | true | Draw ground rings around nearby instanced dungeons |
| Rings | ShowRadiusMeters | 100 | Show a ring while within this distance of the entrance |
How it works
VLR stamps every dungeon it manages with VV_LastReset (the in-game day it last reset)
on the dungeon's LocationProxy world object (ZDO). ZDOs sync to every connected
client, so this mod:
- scans nearby
LocationProxyobjects for the stamp (throttled, default 1 s), - resolves the effective reset interval from VLR's own live config on the client
(which Jotunn keeps synced with the server's settings) or falls back to
FallbackResetDays, - displays
lastReset + interval − now, mirroring VLR's exact day-math, and - (rings) clones the vanilla
CircleProjectormarker used by the workbench and sizes it per dungeon type from the game's location prefab data, confirmed against the live dungeon generator.
The mod never writes world data, owns no networked objects, and patches nothing.
Building from source
Requires the .NET SDK (6+) and a local Valheim install + BepInEx profile:
./build.sh -p:GameDir=/path/to/Valheim -p:ProfileDir=/path/to/BepInExProfileRoot
GameDir is the folder containing valheim_Data/; ProfileDir is the folder
containing BepInEx/core. Output: SunkenCryptTimer/bin/Release/SunkenCryptTimer.dll.
./package.sh additionally builds the Thunderstore zip (requires zip). For the
full release/upload workflow see PUBLISHING.md.
Version compatibility
| Version | Status | |
|---|---|---|
| Valheim | 1.0.16 (Unity 6000.0.75f1) | tested, daily use |
| Valheim | older / newer patch levels | untested; the mod only uses stable APIs plus two reflective reads (EnvMan.m_totalSeconds, Hud.m_instance) — likely fine, not guaranteed |
| BepInEx pack (denikson) | 5.4.2351 | tested |
| Venture Location Reset | 1.1.0 / 1.1.1 | tested; interval mapping mirrors VLR's GetResetTime (re-check when VLR adds location types) |
| Jotunn | any / absent | not referenced |
Known limitation: the per-type reset-interval table in src/ResetTimer.cs only takes
effect when VLR's per-type override mode is on; otherwise VLR's global ResetTime
applies to everything (as on most servers).
License
CHANGELOG
CHANGELOG — valheimmer / SunkenCryptTimer
2026-10-06 — v1.1.8: match status-effect text size, sit below the effect row
- Field feedback on v1.1.7's right-side label: font too large, and the fixed 170 px top offset overlapped the minimap. The label now copies its font size from the status-effect template text (the small text on the Rested/Wet icons) and repositions itself every update just below the live bottom edge of the status-effect row, right edges aligned - so wrapped rows of effect icons push the timer down instead of it overlapping anything. HudOffsetY is now only a fallback offset for the rare case the row can't be located.
2026-10-06 — v1.1.7: right-side label, friendly ground-feature names, no rings on graves
- HUD label moved from top-center to the top-right corner (user preference; the top-center spot collides with event/notice text). Right-aligned, anchored 20 px from the right edge; word wrap on with the rect width clamped to the canvas, so long lines ("Odin's Tomb (-435,-142) - resets in 9d 3h") wrap to extra lines growing downward instead of running off the screen. HudOffsetY now means the offset below the top edge on the right side - raise it if it overlaps the minimap or right-side UI mods.
- Field log showed VLR stamps nearly every zone location on the Stormer server (Grave1, InfestedTree01, SwampHut1/2/5, SwampRuin1, StoneTower1, StoneTowerRuins09, Dolmen01, ShipSetting01, Runestone_, WoodHouse2/4/5, Crypt4, ...), so the timer label was firing for all of them with raw prefab names ("Grave1 (-435,-142) - resets in ...") - and kept cycling the whole way home through meadows, which is why the text "kept updating after leaving the swamp". Ground features now get friendly names (Grave, Infested Tree, Swamp Hut, Swamp Ruin, Stone Tower, Ruined Tower, Shipwreck, Dolmen, Runestone, Abandoned House, Goblin Camp); unknown Runestone_/StoneTowerRuins* variants collapse by prefix.
- Ring bug from the same log: ground features (Grave1, InfestedTree01, SwampHut2, SwampRuin1) each got a phantom 45.3 m ring - the same radius as crypts. ZNetScene.GetPrefab does not resolve their location prefabs, so the radius fallback borrowed a neighbouring crypt's dungeon generator (dense swamp = always one within the 120 m bound). Location prefabs now resolve through ZoneSystem's own location registry (GetPrefab kept as secondary), so ground features correctly resolve to "no ring"; the generator fallback only remains for genuinely unresolvable names ("Dungeon" placeholder, DG_*).
2026-10-06 — v1.1.6: fix duplicate HUD labels (v1.1.5 field regression)
- First field session with v1.1.5's clone-based label showed timer text twice (red top-center + brown right side, sometimes running off the right screen edge) with copies persisting long after leaving the dungeon. Session log: "HUD label ready" fired 34 times in one session (should be once per world), sources alternating between a HUD mod's 'Name' and 'TimeText' panels, each creation followed by "Can't remove CanvasRenderer because TextMeshProUGUI depends on it".
- Cause: the label was created by cloning some other panel's text as its SIBLING and stripping non-TMP components (including the CanvasRenderer TMP needs). Parenting into foreign panels means the label inherits their color (brown/red), anchors to the panel's rect instead of the screen (off-screen), and dies or gets orphaned when those panels rebuild/pool — orphaned copies are unreachable by Hide(), hence the persistent text.
- Fix: only the FONT is taken from a donor label (event-name font if loaded, else any actively rendering label). The label itself is built from scratch as a direct child of the HUD canvas root — true top-center-of-screen anchoring, no component stripping, no CanvasRenderer errors. Style (size + color) comes consistently from the event-name element instead of whatever panel donated the font. Stale copies from earlier HUD rebuilds are swept by name on recreation so only one label can ever exist.
2026-10-06 — v1.1.2: fix invisible HUD text + missing rings on dedicated servers
- First real-server test (Stormer dedicated server, Valheim 1.0.16) showed neither feature; session-log instrumentation added in-between builds pinpointed both causes.
- HUD text invisible: the font copied from the vanilla event-name label was a non-null reference to a not-yet-loaded font asset (Unity 6 lazy loading) — TMP renders nothing and logs "no Font Asset assigned". Fonts are now trusted only when their atlas texture is resident; otherwise the HUD is scanned for any actively rendering label's font, then TMP's default asset, retying each update until one loads (so late font assignment self-heals).
- Rings never created: dungeon names resolved to the generic "Dungeon"
placeholder because the LocationProxy ZDO's
s_locationstring reads empty on dedicated servers (worked on the local test server — why the smoke test passed). Names now come (via reflection) from the proxy's privatem_instance— the spawned location GameObject, whose name isSunkenCrypt4(Clone)— with the ZDO field and a zone-system position match as fallbacks. - Ring lifecycle now logs at Info level once per stage (
zone rings: template ready,ring radius: <prefab> <r>m,ring created: ...,tracked dungeon: '<name>' (via <source>)), so field failures are diagnosable from LogOutput.log without enabling DebugLogging. - Buildable on any machine:
dotnet build -p:GameDir=... -p:ProfileDir=...(first non-124 build; dotnet SDK 9 installed on Machine B for this).
2026-10-06 — GitHub publish + Thunderstore packaging
- Public repo created: https://github.com/altpersona/SunkenCryptTimer (MIT license, gh CLI authenticated by user over device flow, SSH remote via uploaded ed25519 key). Private/local files kept out via .gitignore: DEV_NOTES.md, TODO.md, deploy.sh, setup-local-server.py, .claude/, build artifacts (leak scan: server IP/local paths only ever lived in those).
- Release v1.1.0 published with assets
SunkenCryptTimer-v1.1.0.zip(Thunderstore format: manifest+README+icon+CHANGELOG at root, DLL under Plugins/) and the raw DLL. - README rewritten user-facing (requirements matrix, install, usage incl. VLR approach-trigger semantics, config table, build-from-source, version compatibility).
package.sh(builds the zip),tools/make_icon.py(256×256 icon: timer ring + arched crypt door), manifest description trimmed to 204/250 chars, website_url set to the GitHub repo. Thunderstore upload itself is the remaining user step (TODO.md).
2026-10-05 — v1.1.0: zone rings + deep VLR research
- Research session (all verified against VLR master source, the server-synced config,
and raw game asset dumps):
- Stormer server VLR config:
ResetTime=10in-game days (~5 h real; 1 day ≈ 30 min),OverrideResetTimes=false→ ALL dungeon types use 10, incl. Sunken Crypts (SunkenCrypt4; theCrypt2/3/4names are black-forest burial chambers). - Reset does NOT happen in the background: fires when a modded player approaches
≤100 m horizontal (VLR
RESET_RANGE; watcher gives up >200 m; ~5 s after zone load, 1 s polls, ONE SHOT per zone load). Reset executes on the approaching client (VLR adds its watcher only on non-dedicated instances, needs ZDO ownership). - Blocks ("player activity", horizontal-only checks): player pieces/tombstones/players
within exteriorRadius = 12 m (ground) or dungeon radius = 45.25 m
(hypot(64/2,64/2) of
DG_SunkenCrypt.m_zoneSize=64×64, at interior altitude y≥4000) of the crypt center. Skip defers, never cancels; timer keeps counting. - Terrain: raising land does NOT block resets, but VLR's
TerrainReset.ResetTerrainreverts all terrain mods within the 12 m ground radius on each reset.
- Stormer server VLR config:
- v1.1.0 changes (user decisions: 45 m ring only, instanced dungeons only, same
plugin, visible within 100 m):
- New
SunkenCryptTimer/src/ZoneRing.cs— ring manager (see DEV_NOTES architecture). src/Tracker.cs—FindNearestTrackedDungeon→CollectTrackedDungeons(collects all);NearbyDungeongainedPosition/Uid, nowinternal; HUD label logic extracted toUpdateHudLabel()unchanged.Plugin.cs—Rings/Enabled(true),Rings/ShowRadiusMeters(100) config;PluginVersion1.0.0 → 1.1.0. Same bump inSunkenCryptTimer.csproj(<Version>) andSunkenCryptTimer/manifest.json(version_number, description updated).README.md— zone rings section, config rows, layout line.- Verified: build 0 errors; deploy to Gale profile; local server smoke test clean
(
Loading [SunkenCryptTimer 1.1.0], 0 fatal). In-game visual check pending (user).
- New