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 TimeControl v1.0.2
plugins/TimeControl.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TimeControl")] [assembly: AssemblyDescription("Adjusts the Valheim day/night cycle length via a multiplier, with server-authoritative sync and an exact mod-version gate.")] [assembly: AssemblyProduct("TimeControl")] [assembly: AssemblyCompany("drummercraig")] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("1.0.1")] [assembly: AssemblyVersion("1.0.1.0")] namespace TimeControl; [BepInPlugin("drummercraig.time_control", "TimeControl", "1.0.1")] public class TimeControlPlugin : BaseUnityPlugin { public const string PluginGuid = "drummercraig.time_control"; public const string PluginName = "TimeControl"; public const string PluginVersion = "1.0.1"; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown TimeControlConfig.Log = ((BaseUnityPlugin)this).Logger; TimeControlConfig.Bind(((BaseUnityPlugin)this).Config); ResilientPatcher.ApplyAll(new Harmony("drummercraig.time_control"), Assembly.GetExecutingAssembly(), ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("[TimeControl] v{0} loaded. DayLengthMultiplier={1:0.###} ", "1.0.1", TimeControlConfig.DayLengthMultiplier.Value) + "(local config; server value takes over when connected to a server).")); } } public static class TimeControlConfig { public static ManualLogSource Log; public static ConfigEntry<float> DayLengthMultiplier; public const float MinMultiplier = 0.1f; public const float MaxMultiplier = 100f; public static float ServerMultiplier = -1f; public static float EffectiveMultiplier => Mathf.Clamp((ServerMultiplier >= 0f) ? ServerMultiplier : DayLengthMultiplier.Value, 0.1f, 100f); public static void Bind(ConfigFile config) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown DayLengthMultiplier = config.Bind<float>("General", "DayLengthMultiplier", 4f, new ConfigDescription("Multiplier applied to the in-game day/night cycle length. 1.0 = vanilla (a full day is ~30 real minutes). 2.0 = days take twice as long; 4.0 = ~2 real hours per full day. Day and night both scale together (their ratio is fixed by Valheim and is preserved). When you join a server running TimeControl, the server's value is used instead of this one for that session, so everyone shares the same clock. Range: " + 0.1f + " to " + 100f + ".", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 100f), Array.Empty<object>())); DayLengthMultiplier.SettingChanged += delegate { DayLength.ApplyEffectiveMultiplier(); }; } } public static class DayLength { [HarmonyPatch(typeof(EnvMan), "Awake")] private static class EnvManAwakePatch { private static void Postfix() { ApplyEffectiveMultiplier(); } } private static long _vanillaBaseSec = -1L; public static bool HasBase => _vanillaBaseSec > 0; public static long VanillaBaseSec => _vanillaBaseSec; public static void ApplyEffectiveMultiplier() { try { EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { return; } if (_vanillaBaseSec < 0) { _vanillaBaseSec = instance.m_dayLengthSec; } float effectiveMultiplier = TimeControlConfig.EffectiveMultiplier; long num = (long)((float)_vanillaBaseSec * effectiveMultiplier); if (num < 1) { num = 1L; } if (instance.m_dayLengthSec != num) { instance.m_dayLengthSec = num; ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogInfo((object)($"[TimeControl] Day length set to {num}s " + $"(vanilla base {_vanillaBaseSec}s x {effectiveMultiplier:0.###}).")); } } } catch (Exception arg) { ManualLogSource log2 = TimeControlConfig.Log; if (log2 != null) { log2.LogError((object)$"[TimeControl] ApplyEffectiveMultiplier failed: {arg}"); } } } } public static class NetworkSync { [HarmonyPatch(typeof(ZNet), "OnNewConnection", new Type[] { typeof(ZNetPeer) })] private static class OnNewConnectionPatch { private static void Postfix(ZNetPeer peer, ZNet __instance) { try { if (__instance.IsServer()) { peer.m_rpc.Register<string>("TimeControl_Version", (Action<ZRpc, string>)OnServerReceiveClientVersion); peer.m_rpc.Invoke("TimeControl_Version", new object[1] { "1.0.1" }); } else { _serverSentVersion = false; peer.m_rpc.Register<string>("TimeControl_Version", (Action<ZRpc, string>)OnClientReceiveServerVersion); peer.m_rpc.Register<float>("TimeControl_DayLength", (Action<ZRpc, float>)OnClientReceiveDayLength); peer.m_rpc.Invoke("TimeControl_Version", new object[1] { "1.0.1" }); } } catch (Exception arg) { ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogError((object)$"[TimeControl] OnNewConnection handling failed: {arg}"); } } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class RpcPeerInfoPatch { private static bool Prefix(ZRpc rpc, ZNet __instance) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 try { if (__instance.IsServer()) { if (!_validatedPeers.Contains(rpc)) { ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogWarning((object)"[TimeControl] Disconnecting client without a matching TimeControl version."); } rpc.Invoke("Error", new object[1] { 3 }); return false; } SendDayLengthToPeer(rpc); return true; } if (!_serverSentVersion) { ManualLogSource log2 = TimeControlConfig.Log; if (log2 != null) { log2.LogWarning((object)"[TimeControl] Server is not running TimeControl. Disconnecting (incompatible version)."); } if ((int)ZNet.GetConnectionStatus() != 3) { SetClientConnectionError((ConnectionStatus)3); } if ((Object)(object)Game.instance != (Object)null) { Game.instance.Logout(true, true); } return false; } return true; } catch (Exception arg) { ManualLogSource log3 = TimeControlConfig.Log; if (log3 != null) { log3.LogError((object)$"[TimeControl] RPC_PeerInfo gate failed, allowing vanilla handshake: {arg}"); } return true; } } private static void SendDayLengthToPeer(ZRpc rpc) { try { rpc.Invoke("TimeControl_DayLength", new object[1] { TimeControlConfig.EffectiveMultiplier }); } catch (Exception ex) { ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogWarning((object)("[TimeControl] Failed to push day length: " + ex.Message)); } } } } private const string RpcVersion = "TimeControl_Version"; private const string RpcDayLength = "TimeControl_DayLength"; private static readonly HashSet<ZRpc> _validatedPeers = new HashSet<ZRpc>(); private static bool _serverSentVersion; private static void OnServerReceiveClientVersion(ZRpc rpc, string clientVersion) { try { if (string.Equals(clientVersion, "1.0.1", StringComparison.Ordinal)) { _validatedPeers.Add(rpc); ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogInfo((object)("[TimeControl] Client presented matching version " + clientVersion + ".")); } } else { ManualLogSource log2 = TimeControlConfig.Log; if (log2 != null) { log2.LogWarning((object)("[TimeControl] Client version '" + clientVersion + "' != server '1.0.1'. Will be disconnected at peer-info handshake.")); } } } catch (Exception arg) { ManualLogSource log3 = TimeControlConfig.Log; if (log3 != null) { log3.LogError((object)$"[TimeControl] OnServerReceiveClientVersion failed: {arg}"); } } } private static void OnClientReceiveServerVersion(ZRpc rpc, string serverVersion) { try { _serverSentVersion = true; if (!string.Equals(serverVersion, "1.0.1", StringComparison.Ordinal)) { ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogWarning((object)("[TimeControl] Server version '" + serverVersion + "' != client '1.0.1'. Disconnecting (incompatible version).")); } } else { ManualLogSource log2 = TimeControlConfig.Log; if (log2 != null) { log2.LogInfo((object)("[TimeControl] Server version " + serverVersion + " matches.")); } } } catch (Exception arg) { ManualLogSource log3 = TimeControlConfig.Log; if (log3 != null) { log3.LogError((object)$"[TimeControl] OnClientReceiveServerVersion failed: {arg}"); } } } private static void OnClientReceiveDayLength(ZRpc rpc, float serverMultiplier) { try { TimeControlConfig.ServerMultiplier = serverMultiplier; ManualLogSource log = TimeControlConfig.Log; if (log != null) { log.LogInfo((object)$"[TimeControl] Adopting server day-length multiplier {serverMultiplier:0.###} for this session."); } DayLength.ApplyEffectiveMultiplier(); } catch (Exception arg) { ManualLogSource log2 = TimeControlConfig.Log; if (log2 != null) { log2.LogError((object)$"[TimeControl] OnClientReceiveDayLength failed: {arg}"); } } } private static void SetClientConnectionError(ConnectionStatus status) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) AccessTools.Field(typeof(ZNet), "m_connectionStatus")?.SetValue(null, status); } } internal static class ResilientPatcher { internal static void ApplyAll(Harmony harmony, Assembly assembly, ManualLogSource log) { if (harmony == null || assembly == null) { return; } int num = 0; int num2 = 0; List<string> list = null; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly); foreach (Type type in typesFromAssembly) { try { List<MethodInfo> list2 = harmony.CreateClassProcessor(type).Patch(); if (list2 != null && list2.Count > 0) { num++; if (log != null) { log.LogDebug((object)$"[TimeControl] patched {type.Name} ({list2.Count} method(s))."); } } } catch (Exception ex) { num2++; (list ?? (list = new List<string>())).Add(type.Name); if (log != null) { log.LogWarning((object)("[TimeControl] SKIP patch class " + type.Name + ": " + ex.Message)); } } } if (num2 == 0) { if (log != null) { log.LogInfo((object)$"[TimeControl] patches: {num} applied, 0 skipped."); } } else if (log != null) { log.LogWarning((object)(string.Format("[TimeControl] patches: {0} applied, {1} skipped ({2}). ", num, num2, string.Join(", ", list)) + "A skipped patch usually means a Valheim update changed a hooked method; that feature is disabled but the mod is still running.")); } } }