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.
Decompiled source of Vaka v1.0.0
plugins/Vaka/Vaka.dll
Decompiled 12 hours agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Ezomic.Core; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Thijssen Software")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")] [assembly: AssemblyDescription("Fires keep while you are away. A single absence costs one fuel, however long it was.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f59d61ab2ff2cc72d39205908d3e0d8a0759d11c")] [assembly: AssemblyProduct("Vaka")] [assembly: AssemblyTitle("Vaka")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Vaka { internal static class VakaConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<float> MaxFuelPerAbsence; public static ConfigEntry<bool> Verbose; public static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("Vaka", "Enabled", true, "Whether the cap applies at all. Off leaves the plugin loaded and charging vanilla's full bill for every absence, which is the behaviour this mod exists to change - so off is only useful for telling the two apart without uninstalling."); MaxFuelPerAbsence = config.Bind<float>("Vaka", "MaxFuelPerAbsence", 1f, "How much fuel a fire may lose to a single absence, in the units the hover text counts - logs, resin, whatever that fire burns. One, because a fire you banked full should still be lit when you get back, and one unit off the top is a bill you can pay by walking past it. It is not a discount on burning: a fire you are standing next to consumes exactly what vanilla says, and this number never touches it. 0 makes an absence free. Large numbers restore vanilla, where a fire is charged for every hour that passed whether anyone was there or not. A fire that was nearly empty when you left still goes out - the cap limits what an absence costs, it does not conjure fuel."); Verbose = config.Bind<bool>("Diagnostics", "Verbose", false, "One line per fire every time one comes back from being unloaded, naming the fire, how long it was away, what that was worth in fuel and what it was actually charged - including the times the cap did not apply. This is the only precise readout there is. Hover text draws fuel rounded up to a whole unit, so a fire that lost a third of a log looks identical to one that lost nothing, and both look identical to a mod that never loaded. Quiet while you are near a fire, because a live update is two seconds and is never reported, but a base with thirty torches writes thirty lines the moment you walk into it."); } internal static double CapSeconds(float secondsPerFuel) { if (!Enabled.Value) { return -1.0; } if (secondsPerFuel <= 0f || float.IsNaN(secondsPerFuel) || float.IsInfinity(secondsPerFuel)) { return -1.0; } float num = MaxFuelPerAbsence.Value; if (float.IsNaN(num)) { return -1.0; } if (num < 0f) { num = 0f; } return (double)secondsPerFuel * (double)num; } } internal static class VakaPatches { private const string Target = "GetTimeSinceLastUpdate"; private static readonly ConditionalWeakTable<Fireplace, object> _billed = new ConditionalWeakTable<Fireplace, object>(); private const double LiveInterval = 2.0; private const double WorthReporting = 6.0; internal static void Apply(Harmony harmony) { try { if (AccessTools.Method(typeof(Fireplace), "GetTimeSinceLastUpdate", (Type[])null, (Type[])null) == null) { VakaPlugin.Log.LogError((object)"Vaka is doing nothing: Fireplace.GetTimeSinceLastUpdate is not in this build of the game, so there is nothing to cap and fires burn the vanilla way. That method is private and matched by name, so a game update is the likely reason."); } else { harmony.PatchAll(typeof(VakaPatches)); } } catch (Exception ex) { VakaPlugin.Log.LogError((object)("Vaka is doing nothing: could not patch Fireplace.GetTimeSinceLastUpdate, so fires burn the vanilla way. " + ex.Message)); } } [HarmonyPostfix] [HarmonyPatch(typeof(Fireplace), "GetTimeSinceLastUpdate")] private static void GetTimeSinceLastUpdate(Fireplace __instance, ref double __result) { if (!((Object)(object)__instance == (Object)null) && !_billed.TryGetValue(__instance, out var _)) { _billed.Add(__instance, null); double num = __result; double num2 = VakaConfig.CapSeconds(__instance.m_secPerFuel); if (num2 >= 0.0 && num > num2) { __result = num2; } if (VakaConfig.Verbose.Value) { Report(__instance, num, __result); } } } private static void Report(Fireplace fire, double charged, double billed) { if (!(charged < 6.0) && fire.IsBurning() && !fire.m_infiniteFuel) { string prefabName = Utils.GetPrefabName(((Object)((Component)fire).gameObject).name); float secPerFuel = fire.m_secPerFuel; if (billed >= charged) { VakaPlugin.Log.LogInfo((object)(prefabName + " was away " + Span(charged) + " of world time, worth " + Fuel(charged, secPerFuel) + " fuel. Under the cap, so it was charged in full.")); } else { VakaPlugin.Log.LogInfo((object)(prefabName + " was away " + Span(charged) + " of world time, worth " + Fuel(charged, secPerFuel) + " fuel. Charged " + Span(billed) + ", " + Fuel(billed, secPerFuel) + " fuel.")); } } } private static string Span(double seconds) { if (seconds < 90.0) { return seconds.ToString("0") + "s"; } if (seconds < 5400.0) { return (seconds / 60.0).ToString("0.0") + "m"; } return (seconds / 3600.0).ToString("0.0") + "h"; } private static string Fuel(double seconds, float secondsPerFuel) { if (secondsPerFuel <= 0f) { return "0"; } return (seconds / (double)secondsPerFuel).ToString("0.00"); } } [BepInPlugin("ezomic.valheim.vaka", "Vaka", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class VakaPlugin : BaseUnityPlugin { public const string PluginGuid = "ezomic.valheim.vaka"; public const string PluginName = "Vaka"; public const string PluginVersion = "1.0.0"; public const string PluginAuthor = "Robbin Thijssen"; private const string CoreGuid = "ezomic.valheim.core"; internal static ManualLogSource Log; internal static bool CorePresent; private Harmony _harmony; private void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; VakaConfig.Bind(((BaseUnityPlugin)this).Config); TryRegisterWithCore(); _harmony = new Harmony("ezomic.valheim.vaka"); VakaPatches.Apply(_harmony); Log.LogInfo((object)"Vaka 1.0.0 by Robbin Thijssen - ready."); } private void TryRegisterWithCore() { CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core"); if (!CorePresent) { Log.LogInfo((object)"Core not installed - running standalone, without the version gate."); } else { RegisterWithCore(); } } [MethodImpl(MethodImplOptions.NoInlining)] private void RegisterWithCore() { Suite.Register("ezomic.valheim.vaka", "Vaka", "1.0.0", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null); Suite.Sync((ConfigEntryBase[])(object)new ConfigEntryBase[2] { (ConfigEntryBase)VakaConfig.Enabled, (ConfigEntryBase)VakaConfig.MaxFuelPerAbsence }); Suite.Local((ConfigEntryBase)(object)VakaConfig.Verbose); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } }