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 DayNight4010 v2.0.1
plugins/DayNight4010.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DayNight4010")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.0.0.0")] [assembly: AssemblyInformationalVersion("2.0.0")] [assembly: AssemblyProduct("DayNight4010")] [assembly: AssemblyTitle("DayNight4010")] [assembly: AssemblyVersion("2.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 DayNight4010 { [BepInPlugin("local.daynight4010", "DayNight4010", "2.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "local.daynight4010"; public const string Release = "2.0.0"; private const string SupportedGame = "1.0.12"; private static Plugin instance; private ConfigEntry<bool> modEnabled; private ConfigEntry<double> dayMinutes; private ConfigEntry<double> nightMinutes; private Harmony harmony; private ZNet session; private EnvMan sessionEnvironment; private CalendarClock calendar; private bool failed; private bool sessionChecked; private bool synced; private bool snapPhase; private long observedDayLength; private const string Request = "DayNight4010_CalendarRequest_v2"; private const string Reply = "DayNight4010_CalendarReply_v2"; private ConfigEntry<bool> showClock; private ConfigEntry<string> clockLanguage; private ConfigEntry<int> fontSize; private ConfigEntry<int> offset; private readonly Dictionary<ZRpc, float> clockPeers = new Dictionary<ZRpc, float>(); private readonly Dictionary<ZRpc, float> lastReply = new Dictionary<ZRpc, float>(); private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4]; private ZNet clockNetwork; private float nextPoll; private float snapshotAt; private bool received; private bool active; private bool skipping; private double world; private GUIStyle clockStyle; private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown instance = this; modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable the server calendar. Restart after editing."); dayMinutes = ((BaseUnityPlugin)this).Config.Bind<double>("Cycle", "DayMinutes", 40.0, new ConfigDescription("Real minutes of day. Restart server after editing.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(1.0, 1440.0), Array.Empty<object>())); nightMinutes = ((BaseUnityPlugin)this).Config.Bind<double>("Cycle", "NightMinutes", 10.0, new ConfigDescription("Real minutes of night. Sleep still skips to morning. Restart server after editing.", (AcceptableValueBase)(object)new AcceptableValueRange<double>(1.0, 1440.0), Array.Empty<object>())); BindCountdown(); if (((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString() != "1.0.12") { Stop("Requires Valheim 1.0.12"); return; } try { harmony = new Harmony("local.daynight4010"); Patch("FixedUpdate", "BeforeEnvironment"); Patch("RescaleDayFraction", "ScalePhase", new Type[1] { typeof(float) }); Patch("GetDay", "ReadDay", new Type[1] { typeof(double) }); Patch("GetCurrentDay", "ReadCurrentDay"); Patch("GetMorningStartSec", "ReadMorning", new Type[1] { typeof(int) }); Patch("SkipToMorning", "SkipMorning"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"DayNight4010 2.0.0 loaded: calendar only, normal production time. Required on server and clients."); } catch (Exception ex) { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Stop(ex.ToString()); } } private void Patch(string method, string prefix, Type[] args = null) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(EnvMan), method, args, (Type[])null); if (methodInfo == null) { throw new MissingMethodException("EnvMan." + method); } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), prefix, (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static bool ValidMinutes(double n) { if (!double.IsNaN(n) && !double.IsInfinity(n) && n >= 1.0) { return n <= 1440.0; } return false; } private void Stop(string reason) { failed = true; calendar = null; ((BaseUnityPlugin)this).Logger.LogError((object)("DayNight4010 calendar inactive: " + reason + ". Native world seconds remain unchanged.")); } private void EnsureSession(EnvMan env) { if ((Object)(object)session != (Object)(object)ZNet.instance || (Object)(object)sessionEnvironment != (Object)(object)env) { session = ZNet.instance; sessionEnvironment = env; calendar = null; synced = false; sessionChecked = false; snapPhase = false; observedDayLength = (Object.op_Implicit((Object)(object)env) ? env.m_dayLengthSec : 0); } if (!Object.op_Implicit((Object)(object)session) || !Object.op_Implicit((Object)(object)env) || failed) { return; } if (env.m_dayLengthSec != observedDayLength) { Stop("Competing day-length change detected"); return; } if (!sessionChecked) { string[] source = new string[8] { "longerdays", "mrdaynight", "extendeddaytime", "controltime", "dagrnott", "seasons", "zenworldsettings", "valheimplus" }; foreach (PluginInfo value in Chainloader.PluginInfos.Values) { string text = (value.Metadata.GUID + value.Metadata.Name).ToLowerInvariant().Replace(" ", ""); if (value.Metadata.GUID != "local.daynight4010" && source.Any(text.Contains)) { Stop("Competing clock mod: " + value.Metadata.Name); return; } } string[] array = new string[4] { "RescaleDayFraction", "GetCurrentDay", "SkipToMorning", "GetMorningStartSec" }; foreach (string text2 in array) { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)AccessTools.Method(typeof(EnvMan), text2, (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Owners.Any((string x) => x != "local.daynight4010")) { Stop("Competing calendar patch: " + text2); return; } } Patches patchInfo2 = Harmony.GetPatchInfo((MethodBase)AccessTools.Method(typeof(ZNet), "UpdateNetTime", (Type[])null, (Type[])null)); if (patchInfo2 != null && patchInfo2.Owners.Any()) { Stop("Another mod changes ZNet.UpdateNetTime"); return; } sessionChecked = true; } if (!session.IsServer() || calendar != null || !modEnabled.Value || session.GetNrOfPlayers() == 0 || ZNet.World == null) { return; } try { if (!ValidMinutes(dayMinutes.Value) || !ValidMinutes(nightMinutes.Value)) { throw new InvalidDataException("Invalid durations"); } long uid = ZNet.World.m_uid; string text3 = Path.Combine(Paths.ConfigPath, "DayNight4010", "world-" + uid + ".calendar"); double timeSeconds = session.GetTimeSeconds(); double num = dayMinutes.Value * 60.0; double num2 = nightMinutes.Value * 60.0; CalendarClock calendarClock; if (File.Exists(text3)) { calendarClock = CalendarClock.Parse(File.ReadAllText(text3), uid); if (calendarClock.DayNumber(timeSeconds) < 0) { throw new InvalidDataException("World predates calendar anchor; restore matching calendar backup"); } if (calendarClock.DaySeconds != num || calendarClock.NightSeconds != num2) { calendarClock = calendarClock.Reconfigure(timeSeconds, num, num2); calendarClock.Save(text3, uid); } } else { calendarClock = CalendarClock.FromLegacy(timeSeconds, observedDayLength, num, num2); calendarClock.Save(text3, uid); } calendar = calendarClock; synced = true; snapPhase = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"SERVER CALENDAR ACTIVE: {num / 60.0:0.##} min day / {num2 / 60.0:0.##} min night; normal production seconds; day {calendarClock.DayNumber(timeSeconds)}. State: {text3}"); } catch (Exception ex) { Stop("Cannot load/save calendar: " + ex.Message); } } private static Plugin Ready(EnvMan env) { Plugin plugin = instance; if ((Object)(object)plugin == (Object)null || plugin.failed) { return null; } plugin.EnsureSession(env); if (plugin.calendar == null || plugin.failed) { return null; } return plugin; } private static void BeforeEnvironment(EnvMan __instance, ref float ___m_smoothDayFraction) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin != (Object)null && plugin.snapPhase) { ___m_smoothDayFraction = (float)plugin.calendar.Phase(plugin.session.GetTimeSeconds()); plugin.snapPhase = false; } } private static bool ScalePhase(EnvMan __instance, ref float __result) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin == (Object)null) { return true; } __result = (float)plugin.calendar.Phase(plugin.session.GetTimeSeconds()); return false; } private static bool ReadDay(EnvMan __instance, double time, ref int __result) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin == (Object)null) { return true; } __result = plugin.calendar.DayNumber(time); return false; } private static bool ReadCurrentDay(EnvMan __instance, ref int __result) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin == (Object)null) { return true; } __result = plugin.calendar.DayNumber(plugin.session.GetTimeSeconds()); return false; } private static bool ReadMorning(EnvMan __instance, int day, ref double __result) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin == (Object)null) { return true; } __result = plugin.calendar.Morning(day); return false; } private static bool SkipMorning(EnvMan __instance, ref bool ___m_skipTime, ref double ___m_skipToTime, ref double ___m_timeSkipSpeed) { Plugin plugin = Ready(__instance); if ((Object)(object)plugin == (Object)null) { return true; } if (!plugin.session.IsServer()) { return false; } double timeSeconds = plugin.session.GetTimeSeconds(); ___m_skipToTime = plugin.calendar.NextMorning(timeSeconds); ___m_skipTime = true; ___m_timeSkipSpeed = (___m_skipToTime - timeSeconds) / 12.0; return false; } private void OnDestroy() { ClearCountdown(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } private void BindCountdown() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown showClock = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowCountdown", true, "Display countdown; synchronization stays enabled when hidden."); clockLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("HUD", "Language", "pl", new ConfigDescription("Client language.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "pl", "en" }), Array.Empty<object>())); fontSize = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "FontSize", 20, new ConfigDescription("Client text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 40), Array.Empty<object>())); offset = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "OffsetY", 8, new ConfigDescription("Pixels below minimap.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>())); } private void ClearCountdown() { foreach (ZRpc key in clockPeers.Keys) { key.Unregister("DayNight4010_CalendarRequest_v2"); key.Unregister("DayNight4010_CalendarReply_v2"); } clockPeers.Clear(); lastReply.Clear(); clockNetwork = null; received = false; nextPoll = 0f; } private void RejectPeer(ZNetPeer peer, string reason) { ((BaseUnityPlugin)this).Logger.LogError((object)("DayNight4010 connection rejected: " + reason + ". Install 2.0.0 on server and ALL clients.")); peer.m_rpc.Invoke("Disconnect", Array.Empty<object>()); clockNetwork.Disconnect(peer); } private void Update() { if ((Object)(object)clockNetwork != (Object)(object)ZNet.instance) { ClearCountdown(); clockNetwork = ZNet.instance; } if (!Object.op_Implicit((Object)(object)clockNetwork) || Time.realtimeSinceStartup < nextPoll) { return; } nextPoll = Time.realtimeSinceStartup + 1f; if (Object.op_Implicit((Object)(object)EnvMan.instance)) { EnsureSession(EnvMan.instance); } float realtimeSinceStartup = Time.realtimeSinceStartup; HashSet<ZRpc> hashSet = new HashSet<ZRpc>(); foreach (ZNetPeer item in new List<ZNetPeer>(clockNetwork.GetPeers())) { if (!item.IsReady()) { continue; } ZRpc rpc = item.m_rpc; hashSet.Add(rpc); if (!clockPeers.ContainsKey(rpc)) { clockPeers.Add(rpc, realtimeSinceStartup); if (clockNetwork.IsServer()) { rpc.Register<string>("DayNight4010_CalendarRequest_v2", (Action<ZRpc, string>)SendClock); } else { rpc.Register<ZPackage>("DayNight4010_CalendarReply_v2", (Action<ZRpc, ZPackage>)ReceiveClock); } } if ((!clockNetwork.IsServer() || modEnabled.Value) && (failed || realtimeSinceStartup - clockPeers[rpc] > 30f)) { RejectPeer(item, failed ? "Local calendar failed; see earlier log" : "Missing calendar handshake/heartbeat"); } else if (!clockNetwork.IsServer() && rpc == clockNetwork.GetServerRPC() && !failed) { rpc.Invoke("DayNight4010_CalendarRequest_v2", new object[1] { "2.0.0" }); } } foreach (ZRpc item2 in new List<ZRpc>(clockPeers.Keys)) { if (!hashSet.Contains(item2)) { item2.Unregister("DayNight4010_CalendarRequest_v2"); item2.Unregister("DayNight4010_CalendarReply_v2"); clockPeers.Remove(item2); lastReply.Remove(item2); } } } private void SendClock(ZRpc rpc, string version) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)clockNetwork) || !clockNetwork.IsServer() || !clockPeers.ContainsKey(rpc) || version != "2.0.0") { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!lastReply.TryGetValue(rpc, out var value) || !(realtimeSinceStartup - value < 0.5f)) { lastReply[rpc] = realtimeSinceStartup; clockPeers[rpc] = realtimeSinceStartup; EnvMan val = EnvMan.instance; if (Object.op_Implicit((Object)(object)val)) { EnsureSession(val); } ZPackage val2 = new ZPackage(); val2.Write("2.0.0"); val2.Write(modEnabled.Value); val2.Write(!failed && calendar != null && Object.op_Implicit((Object)(object)val)); val2.Write(Object.op_Implicit((Object)(object)val) && val.IsTimeSkipping()); val2.Write(clockNetwork.GetTimeSeconds()); val2.Write(calendar?.AnchorWorld ?? 0.0); val2.Write(calendar?.AnchorCalendar ?? 0.0); val2.Write(calendar?.DaySeconds ?? 2400.0); val2.Write(calendar?.NightSeconds ?? 600.0); rpc.Invoke("DayNight4010_CalendarReply_v2", new object[1] { val2 }); } } private void ReceiveClock(ZRpc rpc, ZPackage package) { if (!Object.op_Implicit((Object)(object)clockNetwork) || clockNetwork.IsServer() || rpc != clockNetwork.GetServerRPC() || failed || !Object.op_Implicit((Object)(object)EnvMan.instance)) { return; } try { if (package.ReadString() != "2.0.0") { return; } bool flag = package.ReadBool(); bool flag2 = package.ReadBool(); bool flag3 = package.ReadBool(); double num = package.ReadDouble(); CalendarClock calendarClock = new CalendarClock(package.ReadDouble(), package.ReadDouble(), package.ReadDouble(), package.ReadDouble()); if (double.IsNaN(num) || double.IsInfinity(num) || num < 0.0 || calendarClock.DayNumber(num) < 0) { return; } EnsureSession(EnvMan.instance); if (!failed && (!flag || flag2)) { clockPeers[rpc] = Time.realtimeSinceStartup; snapPhase = flag2 && (!synced || calendar == null || calendar.AnchorWorld != calendarClock.AnchorWorld || calendar.AnchorCalendar != calendarClock.AnchorCalendar || calendar.DaySeconds != calendarClock.DaySeconds || calendar.NightSeconds != calendarClock.NightSeconds); calendar = (flag2 ? calendarClock : null); if (!synced) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(flag2 ? "SERVER CALENDAR SYNCED: normal production time" : "Server calendar disabled")); } synced = true; world = num; active = flag2; skipping = flag3; snapshotAt = Time.realtimeSinceStartup; received = true; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected calendar snapshot: " + ex.Message)); } } private string ClockText() { bool flag = clockLanguage.Value == "pl"; if (failed) { if (!flag) { return "Calendar error — check log"; } return "Błąd kalendarza — sprawdź log"; } if (calendar == null) { if (!flag) { return "Waiting for server calendar..."; } return "Oczekiwanie na kalendarz serwera..."; } double num; if (clockNetwork.IsServer()) { if (Object.op_Implicit((Object)(object)EnvMan.instance) && EnvMan.instance.IsTimeSkipping()) { if (!flag) { return "Skipping night..."; } return "Pomijanie nocy..."; } num = clockNetwork.GetTimeSeconds(); } else { if (!received || Time.realtimeSinceStartup - snapshotAt > 8f) { if (!flag) { return "Waiting for server calendar..."; } return "Oczekiwanie na kalendarz serwera..."; } if (!active) { if (!flag) { return "Calendar inactive"; } return "Kalendarz nieaktywny"; } if (skipping) { if (!flag) { return "Skipping night..."; } return "Pomijanie nocy..."; } num = world + (double)Math.Max(0f, Time.realtimeSinceStartup - snapshotAt); } bool isDay; int num2 = (int)Math.Ceiling(Math.Max(0.0, calendar.Remaining(num, out isDay) - 1E-06)); return ((!isDay) ? (flag ? "NOC" : "NIGHT") : (flag ? "DZIEŃ" : "DAY")) + " " + (num2 / 60).ToString("00") + ":" + (num2 % 60).ToString("00"); } private void OnGUI() { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown if (showClock == null || !showClock.Value || !Object.op_Implicit((Object)(object)clockNetwork) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)Minimap.instance) || !Object.op_Implicit((Object)(object)Minimap.instance.m_smallRoot) || !Minimap.instance.m_smallRoot.activeInHierarchy) { return; } RectTransform component = Minimap.instance.m_smallRoot.GetComponent<RectTransform>(); if (Object.op_Implicit((Object)(object)component)) { Canvas componentInParent = ((Component)component).GetComponentInParent<Canvas>(); Camera obj = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); component.GetWorldCorners(corners); Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, corners[0]); Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, corners[3]); if (clockStyle == null) { clockStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4 }; } clockStyle.fontSize = fontSize.Value; clockStyle.normal.textColor = Color.white; float val3 = Math.Max(230f, val2.x - val.x); string text = ClockText(); val3 = Math.Max(val3, clockStyle.CalcSize(new GUIContent(text)).x + 20f); float num = Mathf.Clamp(val2.x - val3, 0f, Math.Max(0f, (float)Screen.width - val3)); float num2 = Mathf.Clamp((float)Screen.height - val.y + (float)offset.Value, 0f, (float)(Screen.height - fontSize.Value - 16)); GUI.Box(new Rect(num, num2, val3, (float)(fontSize.Value + 16)), text, clockStyle); } } } public sealed class CalendarClock { public readonly double AnchorWorld; public readonly double AnchorCalendar; public readonly double DaySeconds; public readonly double NightSeconds; public double Cycle => DaySeconds + NightSeconds; public CalendarClock(double world, double calendar, double day, double night) { Check(world, 0.0, 100000000000000.0); Check(calendar, 0.0, 100000000000000.0); Check(day, 60.0, 86400.0); Check(night, 60.0, 86400.0); AnchorWorld = world; AnchorCalendar = calendar; DaySeconds = day; NightSeconds = night; } private static void Check(double n, double min, double max) { if (double.IsNaN(n) || double.IsInfinity(n) || n < min || n > max) { throw new ArgumentOutOfRangeException("n", "Invalid calendar value"); } } public double At(double world) { return AnchorCalendar + (world - AnchorWorld); } public double PhaseSeconds(double world) { double num = At(world); return num - Math.Floor(num / Cycle) * Cycle; } public int DayNumber(double world) { return checked((int)Math.Floor(At(world) / Cycle)); } public double Phase(double world) { double num = PhaseSeconds(world); double num2 = NightSeconds / 2.0; if (num < num2) { return num / num2 * 0.25; } if (num < num2 + DaySeconds) { return 0.25 + (num - num2) / DaySeconds * 0.5; } return 0.75 + (num - num2 - DaySeconds) / num2 * 0.25; } public double Remaining(double world, out bool isDay) { double num = PhaseSeconds(world); double num2 = NightSeconds / 2.0; double num3 = num2 + DaySeconds; isDay = num >= num2 && num < num3; if (!isDay) { if (!(num < num2)) { return Cycle - num + num2; } return num2 - num; } return num3 - num; } public double Morning(int day) { return AnchorWorld + ((double)day * Cycle + NightSeconds / 2.0 - AnchorCalendar); } public double NextMorning(double world) { int day = checked((int)Math.Floor((At(world) - NightSeconds / 2.0) / Cycle) + 1); return Morning(day); } private static double Position(double phase, double day, double night) { if (phase < 0.25) { return phase / 0.25 * night / 2.0; } if (phase < 0.75) { return night / 2.0 + (phase - 0.25) / 0.5 * day; } return night / 2.0 + day + (phase - 0.75) / 0.25 * night / 2.0; } public static CalendarClock FromLegacy(double world, double baseCycle, double day, double night) { Check(baseCycle, 1.0, 1000000000.0); double num = world % baseCycle / baseCycle; double phase = ((num < 0.15) ? (num / 0.15 * 0.25) : ((num < 0.85) ? (0.25 + (num - 0.15) / 0.7 * 0.5) : (0.75 + (num - 0.85) / 0.15 * 0.25))); double calendar = Math.Floor(world / baseCycle) * (day + night) + Position(phase, day, night); return new CalendarClock(world, calendar, day, night); } public CalendarClock Reconfigure(double world, double day, double night) { double calendar = (double)DayNumber(world) * (day + night) + Position(Phase(world), day, night); return new CalendarClock(world, calendar, day, night); } public string Serialize(long uid) { return string.Join("\n", "DayNight4010-calendar-2", uid.ToString(CultureInfo.InvariantCulture), AnchorWorld.ToString("R", CultureInfo.InvariantCulture), AnchorCalendar.ToString("R", CultureInfo.InvariantCulture), DaySeconds.ToString("R", CultureInfo.InvariantCulture), NightSeconds.ToString("R", CultureInfo.InvariantCulture)); } public static CalendarClock Parse(string text, long uid) { string[] array = text.Replace("\r", "").Trim().Split('\n'); if (array.Length != 6 || array[0] != "DayNight4010-calendar-2" || long.Parse(array[1], CultureInfo.InvariantCulture) != uid) { throw new InvalidDataException("Calendar world/schema mismatch"); } return new CalendarClock(double.Parse(array[2], CultureInfo.InvariantCulture), double.Parse(array[3], CultureInfo.InvariantCulture), double.Parse(array[4], CultureInfo.InvariantCulture), double.Parse(array[5], CultureInfo.InvariantCulture)); } public void Save(string path, long uid) { Directory.CreateDirectory(Path.GetDirectoryName(path)); string text = path + ".tmp"; File.WriteAllText(text, Serialize(uid)); if (File.Exists(path)) { File.Replace(text, path, path + ".bak"); } else { File.Move(text, path); } } } }