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.
Vaka
Fires keep while you are away. A single absence costs one fuel, however long it was.
| Date uploaded | a week ago |
| Version | 1.1.0 |
| Download link | Ezomic-Vaka-1.1.0.zip |
| Downloads | 148 |
| Dependency string | Ezomic-Vaka-1.1.0 |
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.2350README
Vaka
Vaka does two things to fires. It caps what a fireplace loses while nobody is there, so a single absence costs at most one fuel however long it lasted. And lights that burn resin or coal hold each unit twice as long. Cooking fires burn wood, and while you stand at one it burns exactly what vanilla says.
Features
- A fire is charged at most one fuel for any one absence. The amount is configurable.
- A fire that burns resin or coal lasts twice as long per unit, whether you are there or not. Which fuels count and how much longer are both configurable.
- Wood fires burn exactly as vanilla, to the decimal, while you are at them. Sleeping and
skiptimeare charged in full at the fire's own rate, because the fire was loaded and ticking the whole time. - Covers every piece that uses the vanilla
Fireplacecomponent, including fires added by other mods. - No new pieces, no keybinds, no UI, no new saved data. One Harmony postfix on one method.
- A log line each time you enter a world, naming the fires the light rule applies to.
- Optional logging that reports what each catch-up was worth and what it was actually charged.
How it works
A fireplace does not burn in the background. While its zone is loaded,
Fireplace.UpdateFireplace runs every two seconds, asks how long it has been since the last
run, and subtracts that many seconds of fuel. When the zone is unloaded nothing runs and
nothing re-stamps the timestamp on the fire's network object, so the gap keeps growing. The
first update after your base loads back in pays for the whole gap at once. Whether anyone was
present is never part of the sum, so a base of thirty torches is thirty of those bills landing
in the second you walk in.
The clock is world time, not wall time. A dedicated server only advances it while at least one player is connected, and the value is saved into the world file. An empty server is frozen and a single player world resumes exactly where it stopped. What actually drains your base is the hours other people spent online elsewhere on the map, or in single player, the hours you spent across it yourself.
Vaka caps that first bill rather than making all fuel last longer. A longer fuse on every fire would make cooking cheaper while you are stood at the campfire, which was never the problem, and a longer trip next time would still arrive at a cold base.
An absence is recognised by the fireplace object not having been running, not by the size of
the gap. The game creates a fireplace when its zone loads and destroys it when the zone goes,
so the first update for a new object is the one paying off however long it was away. Every
later update is a live tick and is charged in full whatever its size. That is why sleeping
through the night and the skiptime console command still cost full price.
Light fuels
The second rule is a longer fuse, and only for light. A fire whose fuel is on the
LightFuels list burns each unit LightFuelMultiplier times as long. The defaults are resin
and coal, twice as long. It applies all the time, at the fire and away from it.
It stays off cooking fires for the reason the cap is a cap. A longer fuse on a campfire makes cooking cheaper, and nobody asked for that. On a torch it only means fewer rounds of the base with a stack of resin.
A cooking station or cauldron does not ask for a campfire. It looks for the burning area a campfire carries, and the standing brazier carries one under the same name. If the rule reaches a fire with that area and a cooking station fits over it, that spot cooks on the longer fuse. Nobody has tried it. The log line below names any fire the rule reaches that carries the area.
The rule reads what a fire burns rather than which piece it is, so a torch from another mod that burns resin is covered without anything to update. Which pieces burn resin or coal is the game's own data, so each time you enter a world Vaka writes one line to the log naming the fires the rule applies to, grouped by fuel. A fuel on the list that nothing burns is named too, with "no fireplace in this world burns it".
The two rules stack. The cap is counted in fuel, so an absence still costs a torch at most one resin. The torch just has to be left alone twice as long before the bill gets there.
What it covers
Anything carrying the vanilla Fireplace component: every player-buildable fire, and any
modded fire built on the same component. Numbers ripped from the running game, at vanilla
rates:
| Piece | Prefab | Holds | One fuel | Full tank |
|---|---|---|---|---|
| Campfire, iron campfire | fire_pit, fire_pit_iron |
10 | 1h 23m | 13.9h |
| Bonfire | bonfire |
10 | 1h 23m | 13.9h |
| Hearth | hearth |
20 | 1h 23m | 27.8h |
| Wood ground torch | piece_groundtorch_wood |
4 | 2h 47m | 11.1h |
| Standing brazier | piece_brazierfloor01 |
5 | 5h 33m | 27.8h |
| Ground torch | piece_groundtorch |
6 | 5h 33m | 33.3h |
| Blue and green ground torches | piece_groundtorch_blue, _green |
6 | 5h 33m | 33.3h |
| Wall torch | piece_walltorch |
6 | 5h 33m | 33.3h |
Every column is populated world time, not wall clock. Cooking fires burn wood. The wood ground torch, the ground torch and the wall torch burn resin, and the standing brazier burns coal, so the light rule reaches all four at its default. The blue and green torches burn neither.
With the light rule at its default, a fire it reaches doubles both of its last two columns. A wall torch holds a resin for 11h 7m and a full one lasts about 67 hours. A fire burning anything not on the list keeps the numbers above. The log line when you enter a world says which fires the rule reaches in your game.
In vanilla, cooking fires burn four times faster per fuel than torches do, which is why a base goes dark unevenly. A campfire is empty after about fourteen hours of other people playing, a wall torch after thirty-three, so the fires you cook and warm yourself on go out first and the lighting outlasts them. With the light rule at its default the gap is eight times.
Not covered:
- The demister ground torch (
piece_groundtorch_mist). It has noFireplacecomponent and burns no fuel. - Smelters, charcoal kilns, blast furnaces, eitr refineries, ovens and cooking stations. Those are separate components with their own clocks, and a smelter finishing while you are away is something players want. Worth knowing that a cooking station has the same catch-up behaviour and will turn a rack of meat to charcoal over an unload. That is out of scope here.
Nothing refills anything. There is no auto-feeder and no chest access, and a fire that was nearly empty when you left still goes out. The cap limits what an absence costs, it does not conjure fuel, so the habit worth having is to top your fires up before you log off.
One side effect worth knowing: a fire that stays lit stays able to spread. IsBurning gates
the ignite loop as well as the flame, so coming home to a lit fire is coming home to one that
can still catch what is next to it, where vanilla would have let it go out. Every fire in the
table above has a non-zero ignite radius except the wall torch. It only triggers where the game
already allows cinders, so in practice this is an Ashlands concern rather than a Meadows one.
A torch the light rule keeps burning longer can spread for longer too.
Installation
Install through a mod manager, or drop Vaka.dll into BepInEx/plugins.
Requires BepInEx 5.4.2350. Built against Valheim 1.0.7, BepInEx 5.4.23.5 and Harmony 2.9. BepInEx 5 API only, not compatible with BepInEx 6.
Longhouse Core is an optional soft dependency. Vaka runs without it; see Multiplayer for what you lose.
Configuration
BepInEx/config/ezomic.valheim.vaka.cfg, written on first run. Each entry has its full
comment in the file.
| Setting | Section | Default | Effect |
|---|---|---|---|
Enabled |
[Vaka] |
true |
Whether Vaka does anything. Off leaves the plugin loaded with both rules off: every absence is charged vanilla's full bill and every fuel burns at vanilla's rate. Useful for telling the two apart without uninstalling. |
MaxFuelPerAbsence |
[Vaka] |
1 |
How much fuel a fire may lose to a single absence, in the units the hover text counts. 0 makes an absence free. A large number restores vanilla. |
LightFuels |
[Vaka] |
Resin, Coal |
Fuel items whose fires burn longer, by prefab name, separated by commas. Empty turns the light rule off. |
LightFuelMultiplier |
[Vaka] |
2 |
How many times longer each unit of a light fuel burns. 1 turns the light rule off. Anything below 1 counts as 1, so no fire ever burns faster than vanilla. |
Verbose |
[Diagnostics] |
false |
One log line per fire every time one comes back from being unloaded, and each time you enter a world a list of every fireplace with its fuel and burn rate. See Checking that it works. |
MaxFuelPerAbsence is counted in fuel units rather than seconds, so it means the same thing on
a torch burning resin and a hearth burning wood, and on fires added by other mods. It is also
one press of E: refuelling adds exactly one unit per press whatever the fire is. It never
affects a fire you are standing next to, whatever you set it to.
LightFuels takes item prefab names, such as Resin or Coal, not the names shown in game.
The Verbose list prints every fire's fuel under exactly that name, so if you want another
torch covered, find it there and copy its fuel into the list.
BepInEx writes every entry to disk on first run and the saved value beats a new default in code. If a later version changes a default, edit the cfg as part of the same update.
Multiplayer
Everyone needs Vaka, at the same version and build.
Whichever client owns the fireplace's network object does the arithmetic, and ownership moves every two seconds to whichever player's active area covers the fire. One person without Vaka walking up to your base pays the absence off the vanilla way, for everybody, with both logs looking reasonable. Installing it on the server alone does not help: a dedicated server never sets a reference position, so it is never a candidate owner for a player-built fire and there is no host simulation to put the cap in.
The light rule has the same weakness. A torch burns at whatever rate its current owner's game says, so while a player without Vaka owns it, it burns at vanilla speed.
With Longhouse Core installed, Vaka registers with its version check and the server rejects
clients whose version or build id does not match. The host's Enabled, MaxFuelPerAbsence,
LightFuels and LightFuelMultiplier then apply to connected clients in memory, without
writing their own config file, and their values come back on disconnect. Verbose stays local
to each player.
Without Core, Vaka still works on your own machine, but nothing stops a client that lacks it from removing the cap for everyone, or from burning your torches at the vanilla rate.
Checking that it works
Hover text draws fuel rounded up to a whole unit, so a fire that quietly lost a third of a log reads the same as one that lost nothing, and both read the same as a mod that never loaded.
For the light rule, read the line Vaka writes to BepInEx/LogOutput.log when your character
appears in a world. It starts with "Light fuels last 2x as long" and names every fire the rule
applies to, grouped by fuel. If the rule is off, the line says so and why. On a server it
already reflects the host's settings. It is written again if a light setting changes while you
play, whether the host changed it or you did through a config manager. A dedicated server
never writes it, because it has no character of its own.
Set Verbose = true. Every catch-up writes one line to BepInEx/LogOutput.log naming the
fire, how long it was away, what that was worth in fuel and what it was charged, including the
times the cap did not apply. Walk far enough from a fire that its zone unloads, wait a few
minutes, walk back, and either there is a line with numbers in it or the mod is not running.
It stays silent about unlit, switched-off, roofed and infinite-fuel fires, which were never
going to spend anything.
Leaving a world overnight proves nothing in either direction. World time is saved with the world and only advances while somebody is online, so quitting and coming back bills close to zero whether the mod is installed or not.
For a conclusive test, use a throwaway character in a throwaway world, because skiptime
permanently marks a character as having used cheats:
- Place a ground torch outdoors on open flat ground. The cover check raycasts upwards from just above the torch, so anything solid within roughly a metre overhead stops it burning at all, which passes in both arms of the test.
devcommands, thensetfuel 6. You cannot reach a full 6 by hand, because refuelling refuses once the rounded-up value hits the maximum, so a hand-filled torch sits around 5.02.- Walk more than 150m away so the zone unloads. Run
skiptime 200000. Walk back. - With
Enabled = falsethe torch reads 0/6 and is dark. With ittrueit reads 5/6 and is still lit.
Use at least three times the cap. With the light rule on, one resin is 40,000 seconds of a
torch's time, and skiptime 40000 costs it exactly one fuel whether the cap applied or not,
which looks like a failure and is not. Do one skip and let one tick land: several skips in a
row are several separate absences, each capped on its own, which drains the torch and reads as
no cap at all. Running skiptime while standing at the fire shows nothing about the cap,
because that is not an absence.
It does show the light rule. Same throwaway world:
- Place a ground torch outdoors.
devcommands, thensetfuel 6. - Stay next to it and run
skiptime 30000. - With
Enabled = falseit burns 1.5 resin and reads 5/6. With ittrueit burns 0.75 and still reads 6/6.
The torch is loaded the whole time, so the cap plays no part in this one.
Troubleshooting
Nothing in the log and fires still go out. Check the startup lines. Vaka patches a private
method matched by name, and if Fireplace.GetTimeSinceLastUpdate is not in your build of the
game it logs an error saying it is doing nothing and fires burn the vanilla way. A game update
is the likely cause.
A config change had no effect. BepInEx keeps the value saved on first run. Edit the cfg rather than expecting a new default to apply.
Fires still drain on a server. Confirm every connected player has Vaka. Without Longhouse Core nothing enforces that, and one client without it is enough.
Nothing is logged for a short trip. Nothing is trimmed until the bill passes one fuel, which is an hour and a half of populated world time for a cooking fire and eleven hours for a torch burning resin under the light rule. Under that, the bill is already less than the cap allows.
A torch still burns at the vanilla rate. The light rule goes by fuel, not by piece. Check
the log line from when you entered the world: a torch missing from it burns something that is
not on LightFuels. Turn on Verbose to see what it burns. Switched on in game through a
config manager, the full list is written straight away. Set in the cfg file, it appears the
next time you start the game and enter a world. Add that name to LightFuels if you want it
covered.
Bug reports
Discord, or the issue tracker. Useful to attach:
BepInEx/LogOutput.log, ideally from a session withVerbose = true.AppData/LocalLow/IronGate/Valheim/Player.logif a vanilla mechanic broke. Gameplay exceptions land there rather than in LogOutput.- Whether you were on a server or in single player, and whether Longhouse Core was installed.
BepInEx/config/ezomic.valheim.vaka.cfg.- The prefab name of the fire, if it is one specific piece behaving oddly.
Bugs and ideas
Both go to the site. longhouse.thijssensoftware.nl/bugs is for anything broken, and longhouse.thijssensoftware.nl/ideas is for what a mod should do next. You can vote on other people's ideas there as well.
Signing in takes a Steam or Discord account. I work from that list, so the votes decide what I pick up next.
Discord
discord.gg/hJzAVaZ5wb for mod information, updates, support, bug reports and compatibility questions.
Server
There's also a small EU server running the pack if you want somewhere to play. Details are in the Discord.
Part of Longhouse
Vaka ships in the Longhouse modpack, which pins exact versions of its member mods. It behaves the same installed on its own.
Changelog
CHANGELOG.md carries the release history and the reasoning behind the design calls, including why the mod was renamed from Ember before release.
Licence
MIT, see LICENSE. By Robbin Thijssen, Thijssen Software.
CHANGELOG
Changelog
Notable changes to Vaka. Format follows Keep a Changelog, and the mod uses semantic versioning.
[1.1.0] - 2026-09-30
Fires that burn resin or coal last twice as long.
Added
LightFuelsandLightFuelMultiplier. A fire whose fuel item is on the list burns each unit that many times as long, whether you are at it or not. The defaults areResin, Coaland2, so a wall torch holds a resin for 11h 7m of world time instead of 5h 33m. Cooking fires burn wood and are left alone.- A log line each time you enter a world, naming the fires the light rule applies to, grouped
by fuel. A fuel on the list that no fire burns is named too. With
Verboseon there is also a list of every fireplace in the game with its fuel and burn rate. Every player gets the line, whoever is standing at the fires. A dedicated server does not write it.
Changed
Enabledswitches off both rules now, not only the absence cap.
The rule reads each fire's fuel item rather than a list of pieces, so a modded torch that burns
resin is covered as well. In the game as it stands, the log line puts the wall torch, the
ground torch and the wood ground torch on resin, along with Candle_resin,
piece_snowlantern, piece_jackoturnip and CastleKit_groundtorch_unlit. The standing and
hanging braziers are on coal. The blue and green torches burn neither and keep their vanilla
rate. The 1.0 README said the wood
ground torch burns wood. It does not.
A cooking station or cauldron counts any fire with a burning area under it, not only a campfire. Both braziers carry one, so cooking over a brazier would run on the longer fuse. Nobody has tried it. The log line names every fire where that could happen.
It stacks with the cap in fuel. An absence still costs a torch at most one resin. The torch just has to be left twice as long before the bill gets there.
Both settings are new, so an existing config picks up the defaults without editing. With Longhouse Core the host's values apply to everyone, like the cap's. A player without Vaka who owns a torch burns it at the vanilla rate for as long as they own it.
[1.0.1] - 2026-09-12
Changed
- Rewritten README. Same mod, clearer documentation: what it does and how to install it come first, then configuration, multiplayer behaviour, compatibility and troubleshooting. Every config table was checked against the plugin's own Config.Bind calls, so the settings, sections and defaults listed are the ones actually bound. No code changed in this release.
[1.0.0] - 2026-09-04
First release. A fire loses at most one fuel to an absence, however long the absence was.
Named Vaka, having been built as Ember
Vaka is the Old Norse for a vigil, a watch kept through a night nobody else is awake for, which is what the mod asks of a fire. It was written under the name Ember and renamed before any of it was published.
The rename was not taste. Ember collides on Thunderstore - there is already a Valheim
package Ember/ember, plus ember_server and Embers_of_Niflheim - and outside Valheim the
word belongs to Ember.js and to Warframe, which would have hurt the repo and the C# namespace
too. Checking that took some care: Thunderstore's web search silently ignores its ?q=
parameter and returns the default popular listing, so the obvious check reports a false
all-clear. The package index was downloaded whole and grepped instead, and the eight names
shortlisted were all verified clear that way.
The name also stopped describing the mod. Ember is a fire word, and this is not a mod about fire - it never changes what a fire burns while you are standing at it, and its entire subject is the unloaded zone. It sat too close to Kynda, which is already Old Norse for "to kindle". Vaka names the absence instead.
Renaming cost nothing because nothing had shipped and the mod registers no prefabs, so no ZDO
is keyed on a name that would stop resolving - the cost Kynda paid deliberately when its test
pieces died. The config section moved from [Ember] to [Vaka] for the same reason: renaming
a section silently resets every value under it, which would matter if anyone but the author
had a config file, and nobody does.
The line this sits on
It changes what an absence costs. It never changes what burning costs.
While you are near a fire it consumes exactly what vanilla says it consumes, to the same decimal, and this mod can be proven to do nothing at all: a live update measures about two seconds against a cap that is hours away. Everything here happens on the one update that lands when your base loads back in.
Why it is a cap and not a longer fuse
The complaint that started this was a day away and a base of cold fires with nobody having been near them, and the obvious fix is to make fuel last longer. That is wrong twice. It makes a fire cheaper while you are stood at it cooking, which was never the complaint, and it only moves the deadline - a longer trip next time arrives at the same cold base.
What is actually wrong is what a fireplace is charged for. Fireplace.UpdateFireplace bills
against the world clock by diffing ZNet.GetTime() against an s_lastTime stamp on the ZDO,
and while the zone is unloaded nothing re-stamps it. The first update after the zone returns
therefore pays for the entire gap at once, and whether a person was present is not part of
the sum at any point.
The clock is the world's rather than the wall's, which is why the symptom is confusing.
ZNet.UpdateNetTime only advances on a server while GetNrOfPlayers() > 0, and the value is
saved into the world file, so an empty server is frozen and a singleplayer world resumes
where it stopped. What drains a base is the hours other people were online elsewhere on the
map, or the hours you spent across it yourself.
One fuel, and what it does not buy
MaxFuelPerAbsence is counted in fuel units rather than seconds, because every fire has its
own rate and one number in seconds would be four hours of a hearth and a fifth of a torch.
Ripped from this install:
| Fire | Holds | One fuel | Full tank |
|---|---|---|---|
| Campfire, iron campfire | 10 wood | 1h 23m | 13.9h |
| Bonfire | 10 wood | 1h 23m | 13.9h |
| Hearth | 20 wood | 1h 23m | 27.8h |
| Wood ground torch | 4 wood | 2h 47m | 11.1h |
| Standing brazier | 5 resin | 5h 33m | 27.8h |
| Wall torch, ground torch | 6 resin | 5h 33m | 33.3h |
Two things fall out of that. Cooking fires burn four times faster per fuel than torches, so a campfire empties after fourteen hours of other people playing while a wall torch takes thirty-three - which is why a base goes dark unevenly and the fires you cook on go first. And the floor is 5,000 seconds, so no fire has a rate short enough for a cap of one fuel to come anywhere near a two-second tick. The worry that a short-rate prefab would make the cap throttle ordinary burning does not exist in this game.
The unit also happens to be the one the player acts in, which is the argument against
expressing the cap as a fraction of the tank instead. RPC_AddFuel adds exactly 1f per
press whatever the fire is, so "an absence costs at most one press of E" is the same promise
on all six, even though one fuel is a twentieth of a hearth and a quarter of a wood ground
torch.
One unit is a real price and it is paid every time the fire comes back into play. A fire left nearly empty still goes out; the cap limits what an absence costs, it does not conjure fuel. Bank your fires before you log off and they will be lit when you get back.
Everyone needs it, and the reason is ownership
Requirement.Everyone, and the argument is structural rather than cautious. The drain runs
under m_nview.IsOwner() and writes ZDOVars.s_fuel, which is shared world state, and
ZDOMan.ReleaseNearbyZDOS reassigns ownership every two seconds to whichever peer's active
area covers the fire. One player without the plugin walking up to your base takes the fire
over and pays the absence off the vanilla way, for everybody, silently.
Handing it to the host instead is not available: a dedicated server never calls
ZNet.SetReferencePosition - every caller is player code - so its reference position stays at
the world origin and it is never a candidate owner for a player-built fire.
The patch, and why it is that one method
A postfix on the private Fireplace.GetTimeSinceLastUpdate, clamping the double it returns.
That method has exactly one caller, inside vanilla's own IsOwner() && m_secPerFuel > 0f
guard, and its value is used for nothing but the fuel subtraction - so the patch inherits both
guards for free and cannot alter anything else.
It also already re-stamps s_lastTime to now before returning, which means the trimmed
seconds are written off rather than deferred to the next update. That is the behaviour a cap
wants, but it is a consequence of where the patch sits rather than something it chose, so it
is worth stating: a postfix here could not bank the remainder even if that were wanted.
A live tick is never touched, whatever the cap says. Without that guard, setting
MaxFuelPerAbsence to 0 would clamp every two-second tick to zero as well, and "an absence is
free" would quietly mean "fires never burn at all".
An absence is the fire not having run, never a big number
The first version decided what counted as an absence by the size of the gap: anything longer
than three update intervals. That reads as correct and is not. Sleeping runs
EnvMan.UpdateTimeSkip, which pushes the world clock forward at roughly fifty times normal
for a dozen real seconds, and skiptime jumps it instantly - so both arrive as an enormous
gap with the zone loaded and the fire ticking the whole time. A size test forgives them, which
would have made sleeping through the night a free burn on any fire with a short enough rate,
and it would never have shown up on a torch because a torch's cap is five and a half hours.
So the test is now the component's own history. A ConditionalWeakTable records which
fireplaces this machine has already billed; the first bill for an instance is the one paying
for however long the thing was not running, and every later call is a live tick that is never
touched however large it is. ZNetScene destroys and rebuilds a fireplace with its zone, so a
new instance is exactly a return from an absence.
It also closed a hole nobody had noticed: ownership is granted only inside a player's own 64m zone while objects are instantiated across 192m, so a fire 40m away across a zone line loses and regains its owner without ever being destroyed. Under the size test that was a fresh capped "absence" every crossing, and a patrol loop past your own base would have kept it lit for one wood a lap. Under the history test the instance was never destroyed, so it is a live tick and is charged in full.
Verbose reports the catch-ups it did not cap, on purpose
Fuel is drawn as Mathf.Ceil(fuel) out of the maximum, so anything under a whole unit is
invisible in the hover text. That makes three different states look identical from in front of
a fire: the cap worked, nothing had burned down anyway, and the patch never applied. Logging
only the capped ones would have preserved exactly that ambiguity.
So Verbose writes a line on every catch-up, capped or not, and the line carries the span,
what it was worth in fuel and what was actually charged. Walk out of a zone, wait, walk back,
and either there is a line or the mod is not running.
It stays silent about fires that were never going to spend anything, which is the other half
of being truthful. GetTimeSinceLastUpdate is called above vanilla's
IsBurning() && !m_infiniteFuel && state == 1 gate, so an unlit torch, one somebody switched
off, one under a roof and an infinite-fuel brazier all arrive here with a huge gap and nothing
at stake. Reporting those would claim savings that never existed - the same kind of lie the
flag exists to prevent.
A NaN in the config would have killed a fire permanently
Reachable only from a hand-edited cfg, but the damage was to saved world data. BepInEx parses
a float with NumberFormatInfo.InvariantInfo, whose NaNSymbol is the literal NaN. That
would have multiplied through to a NaN cap, and vanilla's subtraction floors fuel at zero
without ever testing for NaN - so the fire's stored fuel becomes NaN, is written into the
world, fails every > 0 test forever, and survives refuelling because Mathf.Clamp leaves
NaN alone. One bad character, one dead fire, no way back without a console command.
CapSeconds now refuses a non-finite rate or amount and hands the fire back to vanilla. The
comparison it feeds was already NaN-safe by accident; being safe on purpose is cheaper than
finding out which.
What has actually been run
The cap works, measured in game on 2026-09-04. A campfire at 10 fuel, teleported away
from so the zone unloaded, skiptime 20000, teleported back:
fire_pit was away 5,6h of world time, worth 4,00 fuel. Charged 83,3m, 1,00 fuel.
Every number in that line is exact against the rip. 5.6h is the 20,000 second skip; 4.00 fuel
is 20,000 divided by the campfire's 5,000 seconds a fuel; 83.3 minutes is 5,000 seconds, which
is the cap to the second. One line, so nothing double-billed on re-entry, and no exception in
either LogOutput.log or Player.log.
That one line is both arms of the test, because it reports what vanilla would have charged
alongside what was charged - so the Enabled = false control run was not needed to make the
claim. It is deliberately in neither build-all.ps1 nor server.ps1.
What is verified, and how: the mechanism, the single call site, the return type and the
absence of any other timed fuel drain on a Fireplace come from a full ILSpy decompile of the
installed assembly_valheim.dll. The Harmony details - that a string-named private target
resolves through DeclaredMethod, that ref double is required to write a return value and
that a plain double __result would silently discard the write - come from reading the
emitter in the installed 0Harmony.dll. The torch numbers come from devkit rips already in
this profile.
Every fireplace prefab in the table above was ripped from the running game on 2026-08-30, which retired the one open question the design hung on: whether some fire had a rate short enough that a one-fuel cap would clamp ordinary ticks. None does - the floor is 5,000 seconds. No code changed as a result; the numbers only confirmed it.
The discriminator was then checked in the other direction, in the same session. The same skip run while standing at the fire, with it loaded and ticking the whole time:
| Test | Fuel | Vaka line | |
|---|---|---|---|
skiptime 20000, zone unloaded |
10 to 9 | logged, 4.00 charged as 1.00 | capped |
skiptime 20000, stood at the fire |
10 to 6 | none | vanilla, untouched |
6/10 is exactly 10 minus 20,000 over 5,000, so the present case is billed at full vanilla rate, and the log still carried exactly one catch-up line for the whole session. That is the half that stops a night's sleep being free burn, and it is the half the first cut of this mod got wrong. Both directions now hold: an absence is the fire not having run, and a large gap on its own is not enough to trigger one.
Still untouched: multiplayer of any kind, ownership moving between clients, the Verbose gate
that is meant to stay silent on unlit and infinite-fuel fires, and every fire type except the
campfire. None of those block calling the mechanism proven.
The README carries the recipe, along with why the obvious test - leave it overnight, come back, look - proves nothing at all in either direction.
What this is worth, stated at the right scale
Vanilla already bounds the loss: fuel floors at zero, so the most an absence of any length can ever cost is the fire's current fuel, and for a torch that is at most six wood. This turns "came back to a dark torch" into "came back one wood down". The wood was never the point - the lap of the base pressing E, and the warmth and Rested source being off, are what an outage actually costs, and that is the thing being bought.
It is also narrower than its own name suggests. A solo logout was never billed at all, because the world clock freezes with the fire, and an empty dedicated server is frozen too. What is left is the two cases that are real on a shared server: other people playing while you were away, and your own long walk across the map.