Sono.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Sono;

internal sealed class ServerRules
{
	internal bool Enabled;

	internal double SleepFromHour;

	internal bool DelayEnabled;

	internal double DelaySeconds;

	internal bool BedPlayersOnly;

	internal bool PermissionAllowed;

	internal double AfkMinutes;

	internal bool CapSpeed;

	internal double MaxSpeed;

	internal bool EffectsAsleep;

	internal bool EffectsAwake;

	internal bool FoodAsleep;

	internal bool FoodAwake;

	internal bool ShowWaitingFor;

	internal int MaxAwake;

	internal bool PermissionUntilDawn;

	internal static ServerRules Defaults()
	{
		ServerRules serverRules = new ServerRules();
		serverRules.Enabled = true;
		serverRules.SleepFromHour = 18.0;
		serverRules.DelayEnabled = false;
		serverRules.DelaySeconds = 10.0;
		serverRules.BedPlayersOnly = true;
		serverRules.PermissionAllowed = true;
		serverRules.AfkMinutes = 1.0;
		serverRules.CapSpeed = true;
		serverRules.MaxSpeed = 60.0;
		serverRules.EffectsAsleep = true;
		serverRules.EffectsAwake = true;
		serverRules.FoodAsleep = false;
		serverRules.FoodAwake = false;
		serverRules.ShowWaitingFor = true;
		serverRules.MaxAwake = 1;
		serverRules.PermissionUntilDawn = true;
		return serverRules;
	}

	internal string Serialize()
	{
		string[] value = new string[17]
		{
			"7",
			B(Enabled),
			D(SleepFromHour),
			B(DelayEnabled),
			D(DelaySeconds),
			B(BedPlayersOnly),
			B(PermissionAllowed),
			D(AfkMinutes),
			B(CapSpeed),
			D(MaxSpeed),
			B(EffectsAsleep),
			B(EffectsAwake),
			B(FoodAsleep),
			B(FoodAwake),
			B(ShowWaitingFor),
			MaxAwake.ToString(CultureInfo.InvariantCulture),
			B(PermissionUntilDawn)
		};
		return string.Join("|", value);
	}

	internal static bool TryDeserialize(string payload, out ServerRules rules)
	{
		rules = null;
		if (string.IsNullOrEmpty(payload))
		{
			return false;
		}
		string[] array = payload.Split('|');
		if (array.Length != 17 || array[0] != "7")
		{
			return false;
		}
		if (!bool.TryParse(array[1], out var result) || !TryDouble(array[2], out var value) || !bool.TryParse(array[3], out var result2) || !TryDouble(array[4], out var value2) || !bool.TryParse(array[5], out var result3) || !bool.TryParse(array[6], out var result4) || !TryDouble(array[7], out var value3) || !bool.TryParse(array[8], out var result5) || !TryDouble(array[9], out var value4) || !bool.TryParse(array[10], out var result6) || !bool.TryParse(array[11], out var result7) || !bool.TryParse(array[12], out var result8) || !bool.TryParse(array[13], out var result9) || !bool.TryParse(array[14], out var result10) || !int.TryParse(array[15], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result11) || !bool.TryParse(array[16], out var result12))
		{
			return false;
		}
		rules = new ServerRules
		{
			Enabled = result,
			SleepFromHour = ClampHour(value),
			DelayEnabled = result2,
			DelaySeconds = Math.Max(0.0, Math.Min(120.0, value2)),
			BedPlayersOnly = result3,
			PermissionAllowed = result4,
			AfkMinutes = Math.Max(0.0, Math.Min(60.0, value3)),
			CapSpeed = result5,
			MaxSpeed = Math.Max(1.0, Math.Min(240.0, value4)),
			EffectsAsleep = result6,
			EffectsAwake = result7,
			FoodAsleep = result8,
			FoodAwake = result9,
			ShowWaitingFor = result10,
			MaxAwake = ClampMaxAwake(result11),
			PermissionUntilDawn = result12
		};
		return true;
	}

	internal static double ClampHour(double hour)
	{
		return Math.Max(0.0, Math.Min(23.99, hour));
	}

	internal static int ClampMaxAwake(int value)
	{
		return Math.Max(0, Math.Min(64, value));
	}

	internal static string B(bool value)
	{
		if (!value)
		{
			return "false";
		}
		return "true";
	}

	internal static string D(double value)
	{
		return value.ToString("R", CultureInfo.InvariantCulture);
	}

	internal static bool TryDouble(string text, out double value)
	{
		return double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
	}
}
internal struct SleeperState
{
	internal long Owner;

	internal bool InBed;

	internal string Name;

	internal bool IgnoresSleep;

	internal SleeperState(long owner, bool inBed, string name, bool ignoresSleep)
	{
		Owner = owner;
		InBed = inBed;
		Name = name;
		IgnoresSleep = ignoresSleep;
	}
}
internal static class SleepCore
{
	internal const double VanillaSleepDelay = 10.0;

	internal const int MaxSubsteps = 240;

	internal const double DawnHour = 6.0;

	internal const double MaxHour = 23.99;

	internal const int MaxAwakeLimit = 64;

	internal static double RescaleDayFraction(double fraction)
	{
		if (fraction >= 0.15 && fraction <= 0.85)
		{
			return 0.25 + (fraction - 0.15) / 0.7 * 0.5;
		}
		if (fraction < 0.5)
		{
			return fraction / 0.15 * 0.25;
		}
		return 0.75 + (fraction - 0.85) / 0.15 * 0.25;
	}

	internal static long DawnIndex(double worldSeconds, double dayLengthSeconds)
	{
		return (long)Math.Floor((worldSeconds - dayLengthSeconds * 0.15) / dayLengthSeconds);
	}

	internal static double ClockHour(double worldSeconds, double dayLengthSeconds)
	{
		double num = worldSeconds % dayLengthSeconds / dayLengthSeconds;
		if (num < 0.0)
		{
			num += 1.0;
		}
		return RescaleDayFraction(Math.Max(0.0, Math.Min(1.0, num))) * 24.0;
	}

	internal static bool SleepAllowedAt(double hour, double fromHour)
	{
		if (fromHour >= 6.0)
		{
			if (!(hour >= fromHour))
			{
				return hour < 6.0;
			}
			return true;
		}
		if (hour >= fromHour)
		{
			return hour < 6.0;
		}
		return false;
	}

	internal static bool EveryoneReady(IList<SleeperState> players, bool permissionAllowed, IDictionary<long, bool> permits, List<long> blockers, List<string> sleepers)
	{
		return EveryoneReady(players, permissionAllowed, 0, permits, blockers, sleepers, null);
	}

	internal static bool EveryoneReady(IList<SleeperState> players, bool permissionAllowed, IDictionary<long, bool> permits, List<long> blockers, List<string> sleepers, List<string> blockerNames)
	{
		return EveryoneReady(players, permissionAllowed, 0, permits, blockers, sleepers, blockerNames);
	}

	internal static bool EveryoneReady(IList<SleeperState> players, bool permissionAllowed, int maxAwake, IDictionary<long, bool> permits, List<long> blockers, List<string> sleepers, List<string> blockerNames)
	{
		List<int> list = new List<int>();
		blockers.Clear();
		sleepers.Clear();
		blockerNames?.Clear();
		for (int i = 0; i < players.Count; i++)
		{
			SleeperState sleeperState = players[i];
			if (sleeperState.InBed)
			{
				sleepers.Add(sleeperState.Name);
			}
			else if (!sleeperState.IgnoresSleep)
			{
				if (!permissionAllowed || !permits.TryGetValue(sleeperState.Owner, out var value) || !value)
				{
					blockers.Add(sleeperState.Owner);
					blockerNames?.Add(sleeperState.Name);
				}
				else
				{
					list.Add(i);
				}
			}
		}
		if (maxAwake > 0 && list.Count > maxAwake)
		{
			for (int j = 0; j < list.Count; j++)
			{
				blockers.Add(players[list[j]].Owner);
				blockerNames?.Add(players[list[j]].Name);
			}
		}
		if (sleepers.Count > 0)
		{
			return blockers.Count == 0;
		}
		return false;
	}

	internal static double CountdownStartValue(double now, ServerRules rules)
	{
		double num = (rules.DelayEnabled ? rules.DelaySeconds : 0.0);
		return now - (10.0 - num);
	}

	internal static bool IsAfk(double now, double lastActivity, double afkMinutes)
	{
		if (afkMinutes > 0.0)
		{
			return now - lastActivity >= afkMinutes * 60.0;
		}
		return false;
	}

	internal static double CappedSkipSpeed(double vanillaSpeed, ServerRules rules)
	{
		if (!rules.Enabled || !rules.CapSpeed)
		{
			return vanillaSpeed;
		}
		return Math.Max(1.0, Math.Min(vanillaSpeed, rules.MaxSpeed));
	}

	internal static float EffectMultiplier(ServerRules rules, bool asleep, bool skipActive, double skipSpeed, double skipEnd, double now)
	{
		if (!(asleep ? rules.EffectsAsleep : rules.EffectsAwake))
		{
			return 1f;
		}
		return SkipMultiplier(rules, skipActive, skipSpeed, skipEnd, now);
	}

	internal static float FoodMultiplier(ServerRules rules, bool asleep, bool skipActive, double skipSpeed, double skipEnd, double now)
	{
		if (!(asleep ? rules.FoodAsleep : rules.FoodAwake))
		{
			return 1f;
		}
		return SkipMultiplier(rules, skipActive, skipSpeed, skipEnd, now);
	}

	private static float SkipMultiplier(ServerRules rules, bool skipActive, double skipSpeed, double skipEnd, double now)
	{
		if (!rules.Enabled || !skipActive || now >= skipEnd)
		{
			return 1f;
		}
		double val = (rules.CapSpeed ? rules.MaxSpeed : skipSpeed);
		return (float)(1.0 + Math.Max(0.0, Math.Min(skipSpeed, val)));
	}

	internal static bool HoldsRealTime(bool asleep, bool isRest, int substep)
	{
		if (asleep && isRest)
		{
			return substep > 0;
		}
		return false;
	}

	internal static void RunSubsteps(float dt, float multiplier, Action<float> step)
	{
		float num = dt * multiplier;
		int num2 = Math.Min(240, Math.Max(1, (int)Math.Ceiling(multiplier)));
		float obj = num / (float)num2;
		for (int i = 0; i < num2; i++)
		{
			step(obj);
		}
	}
}
internal sealed class WaitNotice
{
	private readonly HashSet<long> _inBed = new HashSet<long>();

	private string _lastNames = "";

	internal List<long> Step(IList<SleeperState> players, List<string> blockerNames, bool active, out string names)
	{
		List<string> list = new List<string>(blockerNames);
		list.Sort(StringComparer.OrdinalIgnoreCase);
		names = string.Join(", ", list.ToArray());
		bool flag = active && names.Length > 0;
		if (!flag)
		{
			names = "";
		}
		bool flag2 = names != _lastNames;
		List<long> list2 = new List<long>();
		List<long> list3 = new List<long>();
		for (int i = 0; i < players.Count; i++)
		{
			if (players[i].InBed)
			{
				long owner = players[i].Owner;
				list3.Add(owner);
				if ((flag2 || (flag && !_inBed.Contains(owner))) && !list2.Contains(owner))
				{
					list2.Add(owner);
				}
			}
		}
		_inBed.Clear();
		for (int j = 0; j < list3.Count; j++)
		{
			_inBed.Add(list3[j]);
		}
		_lastNames = names;
		return list2;
	}
}
internal sealed class SleepGate
{
	private bool _wasEligible;

	internal void Reset()
	{
		_wasEligible = false;
	}

	internal int Step(bool eligible)
	{
		int result = 0;
		if (eligible && !_wasEligible)
		{
			result = 1;
		}
		else if (!eligible && _wasEligible)
		{
			result = -1;
		}
		_wasEligible = eligible;
		return result;
	}
}
internal sealed class PermissionState
{
	internal bool Manual;

	private double _lastActivity;

	private bool _sent;

	private bool _sentValue;

	private bool _seenDawn;

	private long _dawn;

	internal PermissionState(double now)
	{
		_lastActivity = now;
	}

	internal void Activity(double now)
	{
		_lastActivity = now;
	}

	internal bool ExpireAtDawn(ServerRules rules, long dawn)
	{
		bool flag = _seenDawn && dawn > _dawn;
		_seenDawn = true;
		_dawn = dawn;
		if (!flag || !Manual || !rules.Enabled || !rules.PermissionUntilDawn)
		{
			return false;
		}
		Manual = false;
		return true;
	}

	internal void Forget()
	{
		_sent = false;
	}

	internal bool Effective(ServerRules rules, double now)
	{
		if (rules.Enabled && rules.PermissionAllowed)
		{
			if (!Manual)
			{
				return SleepCore.IsAfk(now, _lastActivity, rules.AfkMinutes);
			}
			return true;
		}
		return false;
	}

	internal bool NeedsSend(ServerRules rules, double now, out bool value)
	{
		value = Effective(rules, now);
		if (_sent && value == _sentValue)
		{
			return false;
		}
		_sent = true;
		_sentValue = value;
		return true;
	}
}
[BepInPlugin("razaotium.sono", "Sono", "1.5.0")]
public sealed class SonoPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Game), "UpdateSleeping")]
	private static class UpdateSleepingPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Game __instance)
		{
			ServerRules rules = Rules;
			if (!rules.Enabled || SleepingField == null || LastSleepTimeField == null)
			{
				return true;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)EnvMan.instance == (Object)null)
			{
				return false;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			if ((bool)SleepingField.GetValue(__instance))
			{
				if (EnvMan.instance.IsTimeSkipping() || CinematicsManager.IsPlaying())
				{
					return false;
				}
				LastSleepTimeField.SetValue(__instance, timeSeconds);
				SleepingField.SetValue(__instance, false);
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SleepStop", new object[0]);
				return false;
			}
			if (EnvMan.instance.IsTimeSkipping())
			{
				return false;
			}
			string text = WhyNotSleeping(rules);
			bool flag = text.Length == 0;
			if (text != _lastReason)
			{
				if (text.Length > 0 && SleeperNames.Count > 0)
				{
					Log.LogInfo((object)("Not sleeping yet: " + text + "."));
				}
				_lastReason = ((SleeperNames.Count > 0) ? text : "");
			}
			int num = Gate.Step(flag);
			if (num > 0)
			{
				LastSleepTimeField.SetValue(__instance, SleepCore.CountdownStartValue(timeSeconds, rules));
				Log.LogInfo((object)("Everyone is ready: " + SleeperNames.Count + "/" + Players.Count + " in bed."));
			}
			else if (num < 0)
			{
				Log.LogInfo((object)"Sleep cancelled: conditions no longer met.");
			}
			if (!flag)
			{
				return false;
			}
			if (timeSeconds - (double)LastSleepTimeField.GetValue(__instance) < 10.0)
			{
				return false;
			}
			EnvMan.instance.SkipToMorning();
			SleepingField.SetValue(__instance, true);
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SleepStart", new object[0]);
			return false;
		}
	}

	[HarmonyPatch(typeof(Game), "SleepStart")]
	private static class SleepStartPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			Gate.Reset();
			_waitingFor = new string[0];
			ServerRules rules = Rules;
			if (!rules.Enabled || !rules.BedPlayersOnly)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).InBed())
			{
				localPlayer.SetSleeping(true);
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Game), "SleepStop")]
	private static class SleepStopPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			_skipActive = false;
		}
	}

	[HarmonyPatch(typeof(EnvMan), "SkipToMorning")]
	private static class SkipToMorningPatch
	{
		private static void Postfix(EnvMan __instance)
		{
			if (!(TimeSkipSpeedField == null) && !(SkipToTimeField == null))
			{
				double num = (double)TimeSkipSpeedField.GetValue(__instance);
				double num2 = SleepCore.CappedSkipSpeed(num, Rules);
				if (num2 != num)
				{
					TimeSkipSpeedField.SetValue(__instance, num2);
				}
				Log.LogInfo((object)("Skipping to morning at " + num2.ToString("0.##") + "x."));
				_skipActive = true;
				_skipSpeed = num2;
				_skipEnd = (double)SkipToTimeField.GetValue(__instance);
				BroadcastSkipState(0L);
			}
		}
	}

	[HarmonyPatch(typeof(SEMan), "Update")]
	private static class StatusEffectTimePatch
	{
		private static bool _inSubstep;

		internal static bool HoldRest;

		private static bool Prefix(SEMan __instance, ZDO zdo, float dt)
		{
			if (_inSubstep)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !object.ReferenceEquals(__instance, ((Character)localPlayer).GetSEMan()) || (Object)(object)ZNet.instance == (Object)null)
			{
				return true;
			}
			bool asleep = localPlayer.IsSleeping();
			float num = SleepCore.EffectMultiplier(Rules, asleep, _skipActive, _skipSpeed, _skipEnd, ZNet.instance.GetTimeSeconds());
			if (num <= 1f)
			{
				return true;
			}
			_inSubstep = true;
			try
			{
				int substep = 0;
				SleepCore.RunSubsteps(dt, num, delegate(float step)
				{
					HoldRest = SleepCore.HoldsRealTime(asleep, isRest: true, substep++);
					__instance.Update(zdo, step);
				});
			}
			finally
			{
				_inSubstep = false;
				HoldRest = false;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(StatusEffect), "UpdateStatusEffect")]
	private static class RestTimePatch
	{
		private static bool Prefix(StatusEffect __instance)
		{
			if (!StatusEffectTimePatch.HoldRest)
			{
				return true;
			}
			int num = __instance.NameHash();
			if (num != SEMan.s_statusEffectRested)
			{
				return num != SEMan.s_statusEffectResting;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Player), "UpdateFood")]
	private static class FoodTimePatch
	{
		private static readonly Action<Player, float, bool> UpdateFood = AccessTools.MethodDelegate<Action<Player, float, bool>>(AccessTools.Method(typeof(Player), "UpdateFood", (Type[])null, (Type[])null), (object)null, true);

		private static bool _inSubstep;

		private static bool Prefix(Player __instance, float dt, bool forceUpdate)
		{
			if (_inSubstep || forceUpdate || !object.ReferenceEquals(__instance, Player.m_localPlayer) || (Object)(object)ZNet.instance == (Object)null)
			{
				return true;
			}
			float num = SleepCore.FoodMultiplier(Rules, __instance.IsSleeping(), _skipActive, _skipSpeed, _skipEnd, ZNet.instance.GetTimeSeconds());
			if (num <= 1f)
			{
				return true;
			}
			_inSubstep = true;
			try
			{
				SleepCore.RunSubsteps(dt, num, delegate(float step)
				{
					UpdateFood(__instance, step, arg3: false);
				});
			}
			finally
			{
				_inSubstep = false;
			}
			return false;
		}
	}

	public const string PluginGuid = "razaotium.sono";

	public const string PluginName = "Sono";

	public const string PluginVersion = "1.5.0";

	private const string PermitRpc = "Sono_SetPermit";

	private const string RulesRequestRpc = "Sono_RequestRules";

	private const string RulesSyncRpc = "Sono_SyncRules";

	private const string SkipStateRpc = "Sono_SkipState";

	private const string WaitingForRpc = "Sono_WaitingFor";

	private const float RulesRequestInterval = 3f;

	private static readonly Dictionary<long, bool> PermittedByPeer = new Dictionary<long, bool>();

	private static readonly List<SleeperState> Players = new List<SleeperState>();

	private static readonly List<long> Blockers = new List<long>();

	private static readonly List<string> SleeperNames = new List<string>();

	private static readonly List<string> BlockerNames = new List<string>();

	private static readonly SleepGate Gate = new SleepGate();

	private static readonly WaitNotice Notice = new WaitNotice();

	private static readonly int GhostIgnoreSleepHash = "DEV_GhostIgnoreSleep".GetHashCode();

	private static string _lastReason = "";

	private static readonly FieldInfo LastSleepTimeField = AccessTools.Field(typeof(Game), "m_lastSleepTime");

	private static readonly FieldInfo SleepingField = AccessTools.Field(typeof(Game), "m_sleeping");

	private static readonly FieldInfo TimeSkipSpeedField = AccessTools.Field(typeof(EnvMan), "m_timeSkipSpeed");

	private static readonly FieldInfo SkipToTimeField = AccessTools.Field(typeof(EnvMan), "m_skipToTime");

	private static readonly FieldInfo DayLengthField = AccessTools.Field(typeof(EnvMan), "m_dayLengthSec");

	private static readonly MethodInfo GetServerPeerIdMethod = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> ModEnabled;

	internal static ConfigEntry<double> SleepFromHour;

	internal static ConfigEntry<bool> UseSleepDelay;

	internal static ConfigEntry<double> SleepDelaySeconds;

	internal static ConfigEntry<bool> OnlyBedPlayersSleep;

	internal static ConfigEntry<bool> AllowPermission;

	internal static ConfigEntry<double> AfkMinutes;

	internal static ConfigEntry<int> MaxPlayersAwake;

	internal static ConfigEntry<bool> PermissionUntilDawn;

	internal static ConfigEntry<KeyCode> ToggleKey;

	internal static ConfigEntry<bool> CapSkipSpeed;

	internal static ConfigEntry<double> MaximumSkipSpeed;

	internal static ConfigEntry<bool> EffectsWhileAsleep;

	internal static ConfigEntry<bool> EffectsWhileAwake;

	internal static ConfigEntry<bool> FoodWhileAsleep;

	internal static ConfigEntry<bool> FoodWhileAwake;

	internal static ConfigEntry<bool> ShowWaitingFor;

	private static readonly ServerRules DefaultRules = ServerRules.Defaults();

	private static ServerRules _rules = ServerRules.Defaults();

	private static bool _receivedServerRules;

	private static bool _skipActive;

	private static double _skipSpeed = 1.0;

	private static double _skipEnd;

	private Harmony _harmony;

	private float _nextRulesRequest;

	private ZRoutedRpc _registeredRpc;

	private FileSystemWatcher _configWatcher;

	private volatile bool _configReloadRequested;

	private PermissionState _permission;

	private Vector3 _lastMousePosition;

	private static string[] _waitingFor = new string[0];

	private static GUIStyle _panelStyle;

	private static GUIStyle _panelShadow;

	private static ServerRules Rules
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				return _rules;
			}
			if (!_receivedServerRules)
			{
				return DefaultRules;
			}
			return _rules;
		}
	}

	private void Awake()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfiguration();
		ReadRulesFromConfiguration();
		SubscribeToRuleChanges();
		StartConfigWatcher();
		_permission = new PermissionState(Time.unscaledTime);
		_harmony = new Harmony("razaotium.sono");
		_harmony.PatchAll(typeof(SonoPlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Sono 1.5.0 loaded; waiting for the server rules.");
	}

	private void BindConfiguration()
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Expected O, but got Unknown
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Expected O, but got Unknown
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Expected O, but got Unknown
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_021c: Expected O, but got Unknown
		ModEnabled = BindServer("1 - General", "Enabled", value: true, "Turns every Sono change on or off. False restores vanilla sleep. Editing the server CFG while it runs applies to everyone immediately.");
		SleepFromHour = ((BaseUnityPlugin)this).Config.Bind<double>("2 - Starting Sleep", "SleepFromHour", 18.0, new ConfigDescription("Earliest clock hour (24 h, as MyLittleUI shows it) at which the night can be skipped; sleep is allowed from this hour until dawn at 6:00. 18 = nightfall (only at night), 12 = vanilla (afternoon too), 21.5 = 21:30. [SERVER RULE]", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 23.99), new object[0]));
		UseSleepDelay = BindServer("2 - Starting Sleep", "UseSleepDelay", value: false, "Waits SleepDelaySeconds before skipping to morning. Off by default: like vanilla, the night skips as soon as everyone is ready.");
		SleepDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<double>("2 - Starting Sleep", "SleepDelaySeconds", 10.0, new ConfigDescription("Seconds between the conditions being met and the skip to morning. Vanilla has no such wait: sleep starts almost as soon as everyone lies down. 0 matches vanilla. [SERVER RULE]", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 120.0), new object[0]));
		OnlyBedPlayersSleep = BindServer("3 - Experience", "OnlyPlayersInBedSleep", value: true, "Only players in bed get the sleep screen, text, dream and Rested bonus. False uses vanilla SleepStart for everyone.");
		AllowPermission = BindServer("4 - Permission", "AllowPermission", value: true, "Like vanilla, the night only skips when every player is in bed, but a player can press the key to let the others sleep without them. False requires everyone in bed, exactly like vanilla.");
		AfkMinutes = ((BaseUnityPlugin)this).Config.Bind<double>("4 - Permission", "AfkMinutes", 1.0, new ConfigDescription("A player with no mouse or keyboard input for this many minutes counts as letting the others sleep, until they move again. 0 turns it off. [SERVER RULE]", (AcceptableValueBase)(object)new AcceptableValueRange<double>(0.0, 60.0), new object[0]));
		MaxPlayersAwake = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Permission", "MaxPlayersAwake", 1, new ConfigDescription("How many players may stay awake (F7 or AFK) while the others sleep. With more awake than this, the night waits until enough of them go to bed. 0 = no limit. [SERVER RULE]", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), new object[0]));
		PermissionUntilDawn = BindServer("4 - Permission", "PermissionUntilDawn", value: true, "The key's permission lasts one night: at dawn it is dropped and the player must press the key again the next night. False keeps it until pressed again. AFK is not affected.");
		ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("4 - Permission", "TogglePermissionKey", (KeyCode)288, "Local client setting, not enforced by the server. Toggles whether the others may sleep without you.");
		CapSkipSpeed = BindServer("5 - Time", "CapSkipSpeed", value: true, "Caps the skip-to-morning speed at MaximumSkipSpeed. Vanilla skips the rest of the night in 12 real seconds at a constant speed and stops at dawn.");
		MaximumSkipSpeed = ((BaseUnityPlugin)this).Config.Bind<double>("5 - Time", "MaximumSkipSpeed", 60.0, new ConfigDescription("Maximum world seconds per real second when CapSkipSpeed=true. Night is 30% of the day, so from nightfall vanilla skips at about 45x with a 30-minute day; only a lower cap makes the skip longer. [SERVER RULE]", (AcceptableValueBase)(object)new AcceptableValueRange<double>(1.0, 240.0), new object[0]));
		EffectsWhileAsleep = BindServer("6 - Consequences", "StatusEffectsWhileAsleep", value: true, "Players asleep through the skip: their buffs, debuffs and ticks run down with the skipped night. Rested and Resting never do while asleep, like vanilla, and Rested is refreshed on waking.");
		EffectsWhileAwake = BindServer("6 - Consequences", "StatusEffectsWhileAwake", value: true, "Players awake during the skip (F7 or AFK): their buffs, debuffs and ticks run down with the skipped night, Rested included. They get no new Rested.");
		FoodWhileAsleep = BindServer("6 - Consequences", "FoodWhileAsleep", value: false, "Players asleep through the skip: their food runs down with the skipped night (food regen included). False leaves it untouched, like vanilla.");
		FoodWhileAwake = BindServer("6 - Consequences", "FoodWhileAwake", value: false, "Players awake during the skip (F7 or AFK): their food runs down with the skipped night (food regen included). False leaves it untouched, like vanilla.");
		ShowWaitingFor = BindServer("7 - Messages", "ShowWaitingFor", value: true, "While a player lies in bed at sleep time, a small panel in the centre, just below the crosshair, lists who is still awake without permission. It updates as people lie down or press the permission key.");
	}

	private ConfigEntry<bool> BindServer(string section, string key, bool value, string description)
	{
		return ((BaseUnityPlugin)this).Config.Bind<bool>(section, key, value, description + " [SERVER RULE]");
	}

	private void SubscribeToRuleChanges()
	{
		((BaseUnityPlugin)this).Config.SettingChanged += OnServerRuleChanged;
		((BaseUnityPlugin)this).Config.ConfigReloaded += OnServerConfigReloaded;
	}

	private void StartConfigWatcher()
	{
		string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
		string directoryName = Path.GetDirectoryName(configFilePath);
		string fileName = Path.GetFileName(configFilePath);
		if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName))
		{
			_configWatcher = new FileSystemWatcher(directoryName, fileName);
			_configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
			_configWatcher.Changed += OnConfigFileChanged;
			_configWatcher.Created += OnConfigFileChanged;
			_configWatcher.Renamed += OnConfigFileRenamed;
			_configWatcher.EnableRaisingEvents = true;
		}
	}

	private void OnConfigFileChanged(object sender, FileSystemEventArgs args)
	{
		_configReloadRequested = true;
	}

	private void OnConfigFileRenamed(object sender, RenamedEventArgs args)
	{
		_configReloadRequested = true;
	}

	private void OnServerRuleChanged(object sender, SettingChangedEventArgs args)
	{
		ApplyAndBroadcastServerRules();
	}

	private void OnServerConfigReloaded(object sender, EventArgs args)
	{
		ApplyAndBroadcastServerRules();
	}

	private static void ApplyAndBroadcastServerRules()
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			ReadRulesFromConfiguration();
			BroadcastRules();
		}
	}

	private static void ReadRulesFromConfiguration()
	{
		ServerRules serverRules = new ServerRules();
		serverRules.Enabled = ModEnabled.Value;
		serverRules.SleepFromHour = SleepFromHour.Value;
		serverRules.DelayEnabled = UseSleepDelay.Value;
		serverRules.DelaySeconds = SleepDelaySeconds.Value;
		serverRules.BedPlayersOnly = OnlyBedPlayersSleep.Value;
		serverRules.PermissionAllowed = AllowPermission.Value;
		serverRules.AfkMinutes = AfkMinutes.Value;
		serverRules.CapSpeed = CapSkipSpeed.Value;
		serverRules.MaxSpeed = MaximumSkipSpeed.Value;
		serverRules.EffectsAsleep = EffectsWhileAsleep.Value;
		serverRules.EffectsAwake = EffectsWhileAwake.Value;
		serverRules.FoodAsleep = FoodWhileAsleep.Value;
		serverRules.FoodAwake = FoodWhileAwake.Value;
		serverRules.ShowWaitingFor = ShowWaitingFor.Value;
		serverRules.MaxAwake = ServerRules.ClampMaxAwake(MaxPlayersAwake.Value);
		serverRules.PermissionUntilDawn = PermissionUntilDawn.Value;
		_rules = serverRules;
		_receivedServerRules = true;
	}

	private void OnDestroy()
	{
		if (_configWatcher != null)
		{
			_configWatcher.EnableRaisingEvents = false;
			_configWatcher.Dispose();
			_configWatcher = null;
		}
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}

	private void Update()
	{
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		RegisterRpcIfNeeded();
		if (_configReloadRequested)
		{
			_configReloadRequested = false;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					Log.LogInfo((object)"CFG reloaded live; rules sent to clients.");
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Failed to reload CFG live: " + ex.Message));
				}
			}
		}
		UpdateServerSkipState();
		RequestRulesWhenConnected();
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			TrackActivity();
			ServerRules rules = Rules;
			if (ToggleKey != null && Input.GetKeyDown(ToggleKey.Value))
			{
				TogglePermission(rules);
			}
			ExpirePermissionAtDawn(rules);
			SyncPermission(rules);
		}
	}

	private void TrackActivity()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		Vector3 mousePosition = Input.mousePosition;
		if (!Input.anyKey && !(mousePosition != _lastMousePosition))
		{
			Vector2 mouseScrollDelta = Input.mouseScrollDelta;
			if (!(((Vector2)(ref mouseScrollDelta)).sqrMagnitude > 0f))
			{
				goto IL_0040;
			}
		}
		_permission.Activity(Time.unscaledTime);
		goto IL_0040;
		IL_0040:
		_lastMousePosition = mousePosition;
	}

	private static string T(string english, string portuguese)
	{
		if (!IsPortuguese())
		{
			return english;
		}
		return portuguese;
	}

	private static bool IsPortuguese()
	{
		if (Localization.instance == null)
		{
			return false;
		}
		return Localization.instance.GetSelectedLanguage()?.StartsWith("Portuguese", StringComparison.OrdinalIgnoreCase) ?? false;
	}

	private void TogglePermission(ServerRules rules)
	{
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		if (!rules.Enabled || !rules.PermissionAllowed)
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, T("The server requires everyone in bed; there is nothing to toggle.", "O servidor exige todos na cama; não há o que alternar."), 0, (Sprite)null, false);
			return;
		}
		_permission.Manual = !_permission.Manual;
		string text = (_permission.Manual ? T("the others MAY sleep without you", "os outros PODEM dormir sem você") : T("the others must WAIT for you", "os outros devem ESPERAR por você"));
		((Character)Player.m_localPlayer).Message((MessageType)2, string.Concat(T("Sleep: ", "Sono: "), text, T(". Press ", ". Aperte "), ToggleKey.Value, T(" to change.", " para mudar.")), 0, (Sprite)null, false);
	}

	private void ExpirePermissionAtDawn(ServerRules rules)
	{
		if (!((Object)(object)EnvMan.instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && !(DayLengthField == null))
		{
			long num = (long)DayLengthField.GetValue(EnvMan.instance);
			if (num > 0)
			{
				_permission.ExpireAtDawn(rules, SleepCore.DawnIndex(ZNet.instance.GetTimeSeconds(), num));
			}
		}
	}

	private void SyncPermission(ServerRules rules)
	{
		if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && (ZNet.instance.IsServer() || _receivedServerRules) && _permission.NeedsSend(rules, Time.unscaledTime, out var value))
		{
			if (ZNet.instance.IsServer())
			{
				SetPermitted(ZNet.GetUID(), value);
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Sono_SetPermit", new object[1] { value });
		}
	}

	private void RegisterRpcIfNeeded()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && !object.ReferenceEquals(instance, _registeredRpc))
		{
			instance.Register<bool>("Sono_SetPermit", (Action<long, bool>)RpcSetPermit);
			instance.Register("Sono_RequestRules", (Action<long>)RpcRequestRules);
			instance.Register<string>("Sono_SyncRules", (Action<long, string>)RpcReceiveRules);
			instance.Register<string>("Sono_SkipState", (Action<long, string>)RpcReceiveSkipState);
			instance.Register<string>("Sono_WaitingFor", (Action<long, string>)RpcReceiveWaitingFor);
			_registeredRpc = instance;
			_skipActive = false;
			_permission.Forget();
			PermittedByPeer.Clear();
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				ReadRulesFromConfiguration();
				BroadcastRules();
			}
			else
			{
				_receivedServerRules = false;
				_nextRulesRequest = 0f;
			}
		}
	}

	private void RequestRulesWhenConnected()
	{
		if (!_receivedServerRules && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer())
		{
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null && serverPeer.IsReady() && !(Time.unscaledTime < _nextRulesRequest))
			{
				_nextRulesRequest = Time.unscaledTime + 3f;
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Sono_RequestRules", new object[0]);
			}
		}
	}

	private static void UpdateServerSkipState()
	{
		if (_skipActive && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && (!((Object)(object)EnvMan.instance != (Object)null) || !EnvMan.instance.IsTimeSkipping()))
		{
			_skipActive = false;
			BroadcastSkipState(0L);
		}
	}

	private static void RpcSetPermit(long sender, bool permit)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			SetPermitted(sender, permit);
		}
	}

	private static void SetPermitted(long peerId, bool permit)
	{
		PermittedByPeer[peerId] = permit;
		Log.LogInfo((object)("Sleep permission from " + peerId + ": " + (permit ? "may sleep without them" : "wait for them")));
	}

	private static void RpcRequestRules(long sender)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Sono_SyncRules", new object[1] { _rules.Serialize() });
			if (_skipActive)
			{
				BroadcastSkipState(sender);
			}
		}
	}

	private static void BroadcastSkipState(long target)
	{
		if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			string text = (_skipActive ? "1" : "0") + "|" + ServerRules.D(_skipSpeed) + "|" + ServerRules.D(_skipEnd);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "Sono_SkipState", new object[1] { text });
		}
	}

	private static bool IsFromServer(long sender)
	{
		long serverPeerId = GetServerPeerId();
		if (serverPeerId != 0)
		{
			return sender == serverPeerId;
		}
		return true;
	}

	private static void RpcReceiveSkipState(long sender, string payload)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && IsFromServer(sender))
		{
			string[] array = (payload ?? "").Split('|');
			if (array.Length != 3 || !ServerRules.TryDouble(array[1], out var value) || !ServerRules.TryDouble(array[2], out var value2))
			{
				Log.LogWarning((object)"Invalid time-skip packet; ignored.");
				return;
			}
			_skipActive = array[0] == "1";
			_skipSpeed = Math.Max(1.0, Math.Min(240.0, value));
			_skipEnd = value2;
		}
	}

	private static void RpcReceiveWaitingFor(long sender, string names)
	{
		if (IsFromServer(sender))
		{
			_waitingFor = (string.IsNullOrEmpty(names) ? new string[0] : names.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries));
		}
	}

	private void OnGUI()
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Expected O, but got Unknown
		//IL_00e4: 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
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Expected O, but got Unknown
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		if (_waitingFor.Length == 0)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).InBed() && !localPlayer.IsSleeping() && Rules.Enabled && Rules.ShowWaitingFor && (!((Object)(object)Hud.instance != (Object)null) || Hud.instance.m_rootObject.activeSelf))
		{
			float num = (float)Screen.height / 1080f;
			int num2 = Mathf.Max(10, Mathf.RoundToInt(19f * num));
			if (_panelStyle == null || _panelStyle.fontSize != num2)
			{
				GUIStyle val = new GUIStyle(GUI.skin.label);
				val.fontSize = num2;
				val.wordWrap = false;
				val.richText = true;
				val.alignment = (TextAnchor)1;
				_panelStyle = val;
				_panelStyle.normal.textColor = new Color(0.92f, 0.88f, 0.78f);
				_panelShadow = new GUIStyle(_panelStyle);
				_panelShadow.normal.textColor = new Color(0f, 0f, 0f, 0.85f);
			}
			string text = "<b>" + T("Waiting for:", "Esperando por:") + "</b>";
			for (int i = 0; i < _waitingFor.Length; i++)
			{
				text = text + "\n" + _waitingFor[i];
			}
			GUIContent val2 = new GUIContent(text);
			float num3 = 600f * num;
			float num4 = _panelStyle.CalcHeight(val2, num3);
			Rect val3 = default(Rect);
			((Rect)(ref val3))..ctor(((float)Screen.width - num3) * 0.5f, (float)Screen.height * 0.57f, num3, num4);
			GUI.Label(new Rect(((Rect)(ref val3)).x + 2f, ((Rect)(ref val3)).y + 2f, ((Rect)(ref val3)).width, ((Rect)(ref val3)).height), val2, _panelShadow);
			GUI.Label(val3, val2, _panelStyle);
		}
	}

	private static void TellSleepers(ServerRules rules, bool sleepTime)
	{
		string names;
		List<long> list = Notice.Step(Players, BlockerNames, rules.ShowWaitingFor && sleepTime, out names);
		if (list.Count != 0 && ZRoutedRpc.instance != null)
		{
			for (int i = 0; i < list.Count; i++)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(list[i], "Sono_WaitingFor", new object[1] { names });
			}
			if (names.Length > 0)
			{
				Log.LogInfo((object)("Told " + list.Count + " player(s) in bed they are waiting for: " + names + "."));
			}
		}
	}

	private static void BroadcastRules()
	{
		if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Sono_SyncRules", new object[1] { _rules.Serialize() });
		}
	}

	private static void RpcReceiveRules(long sender, string payload)
	{
		if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
		{
			return;
		}
		if (!IsFromServer(sender))
		{
			Log.LogWarning((object)"Rules packet ignored: sender is not the server.");
			return;
		}
		if (!ServerRules.TryDeserialize(payload, out var rules))
		{
			Log.LogWarning((object)"Invalid rules packet from the server; keeping the previous rules.");
			return;
		}
		bool flag = _receivedServerRules && _rules.Enabled != rules.Enabled;
		_rules = rules;
		_receivedServerRules = true;
		if (flag && (Object)(object)Player.m_localPlayer != (Object)null)
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, T("Sono " + (rules.Enabled ? "ENABLED" : "DISABLED") + " for the whole server.", "Sono " + (rules.Enabled ? "LIGADO" : "DESLIGADO") + " para o servidor todo."), 0, (Sprite)null, false);
		}
		Log.LogInfo((object)"Sono rules received from the server.");
	}

	private static long GetServerPeerId()
	{
		if (ZRoutedRpc.instance == null || GetServerPeerIdMethod == null)
		{
			return 0L;
		}
		return (long)GetServerPeerIdMethod.Invoke(ZRoutedRpc.instance, null);
	}

	private static string WhyNotSleeping(ServerRules rules)
	{
		Players.Clear();
		List<ZDO> allCharacterZDOS = ZNet.instance.GetAllCharacterZDOS();
		for (int i = 0; i < allCharacterZDOS.Count; i++)
		{
			ZDO val = allCharacterZDOS[i];
			Players.Add(new SleeperState(val.GetOwner(), val.GetBool(ZDOVars.s_inBed, false), val.GetString(ZDOVars.s_playerName, "?"), val.GetBool(GhostIgnoreSleepHash, false)));
		}
		bool flag = SleepCore.EveryoneReady(Players, rules.PermissionAllowed, rules.MaxAwake, PermittedByPeer, Blockers, SleeperNames, BlockerNames);
		double hour;
		bool flag2 = (TryGetClockHour(out hour) ? SleepCore.SleepAllowedAt(hour, rules.SleepFromHour) : EnvMan.IsNight());
		TellSleepers(rules, flag2);
		if (!flag2)
		{
			return "too early (sleep from " + FormatHour(rules.SleepFromHour) + ")";
		}
		if (!flag)
		{
			if (SleeperNames.Count != 0)
			{
				return "awake without permission or over MaxPlayersAwake: " + string.Join(", ", BlockerNames.ToArray());
			}
			return "nobody in bed";
		}
		return "";
	}

	private static bool TryGetClockHour(out double hour)
	{
		hour = 0.0;
		if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || DayLengthField == null)
		{
			return false;
		}
		long num = (long)DayLengthField.GetValue(EnvMan.instance);
		if (num <= 0)
		{
			return false;
		}
		hour = SleepCore.ClockHour(ZNet.instance.GetTimeSeconds(), num);
		return true;
	}

	private static string FormatHour(double hour)
	{
		int num = (int)Math.Floor(hour * 60.0 + 0.5) % 1440;
		return (num / 60).ToString("00") + ":" + (num % 60).ToString("00");
	}
}