Decompiled source of TimeControl v1.0.0

plugins/TimeControl.dll

Decompiled a day ago
using 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.0")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TimeControl;

[BepInPlugin("drummercraig.time_control", "TimeControl", "1.0.0")]
public class TimeControlPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "drummercraig.time_control";

	public const string PluginName = "TimeControl";

	public const string PluginVersion = "1.0.0";

	private void Awake()
	{
		TimeControlConfig.Log = ((BaseUnityPlugin)this).Logger;
		TimeControlConfig.Bind(((BaseUnityPlugin)this).Config);
		Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("[TimeControl] v{0} loaded. DayLengthMultiplier={1:0.###} ", "1.0.0", 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()
	{
		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.###})."));
			}
		}
	}
}
public static class NetworkSync
{
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	private static class OnNewConnectionPatch
	{
		private static void Postfix(ZNetPeer peer, ZNet __instance)
		{
			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.0" });
			}
			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.0" });
			}
		}
	}

	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	private static class RpcPeerInfoPatch
	{
		private static bool Prefix(ZRpc rpc, ZNet __instance)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Invalid comparison between Unknown and I4
			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;
		}

		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)
	{
		if (string.Equals(clientVersion, "1.0.0", 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.0'. Will be disconnected at peer-info handshake."));
			}
		}
	}

	private static void OnClientReceiveServerVersion(ZRpc rpc, string serverVersion)
	{
		_serverSentVersion = true;
		if (!string.Equals(serverVersion, "1.0.0", StringComparison.Ordinal))
		{
			ManualLogSource log = TimeControlConfig.Log;
			if (log != null)
			{
				log.LogWarning((object)("[TimeControl] Server version '" + serverVersion + "' != client '1.0.0'. Disconnecting (incompatible version)."));
			}
		}
		else
		{
			ManualLogSource log2 = TimeControlConfig.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("[TimeControl] Server version " + serverVersion + " matches."));
			}
		}
	}

	private static void OnClientReceiveDayLength(ZRpc rpc, float serverMultiplier)
	{
		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();
	}

	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);
	}
}