Decompiled source of AtlyssDedicatedServer v0.1.0

plugins/FleesDedicatedServer.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using AtlyssDedicatedServer.HarmonyPatches;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using Steamworks;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("BepInEx")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FleesDedicatedServer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+05665ebd994e2f33b7f66369d861ea754e063df0")]
[assembly: AssemblyProduct("DedicatedServer")]
[assembly: AssemblyTitle("FleesDedicatedServer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 AtlyssDedicatedServer
{
	public class ConsoleListenerWithInput : ILogListener, IDisposable
	{
		[CompilerGenerated]
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private ConsoleLogListener <original>P;

		public readonly StringBuilder Input;

		private static readonly object _consoleLock = new object();

		private static string WindowWidthString = "";

		public ConsoleListenerWithInput(ConsoleLogListener original)
		{
			<original>P = original;
			Input = new StringBuilder();
			base..ctor();
		}

		public void Dispose()
		{
			<original>P.Dispose();
		}

		public void LogEvent(object sender, LogEventArgs eventArgs)
		{
			ClearInputLine();
			<original>P.LogEvent(sender, eventArgs);
			DrawInputPrompt();
		}

		private void ClearInputLine()
		{
			int num = Math.Max(Console.WindowWidth - 1, 0);
			if (WindowWidthString.Length != num)
			{
				WindowWidthString = new string(' ', num);
			}
			ConsoleManager.StandardOutStream.Write('\r');
			ConsoleManager.StandardOutStream.Write(WindowWidthString);
			ConsoleManager.StandardOutStream.Write('\r');
		}

		private void DrawInputPrompt()
		{
			lock (_consoleLock)
			{
				ConsoleManager.StandardOutStream.Write("> ");
				ConsoleManager.StandardOutStream.Write(Input.ToString());
				ConsoleManager.StandardOutStream.Flush();
			}
		}

		public void ProcessInput()
		{
			if (!Console.KeyAvailable)
			{
				return;
			}
			ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(intercept: true);
			lock (_consoleLock)
			{
				switch (consoleKeyInfo.Key)
				{
				case ConsoleKey.Enter:
				{
					string text = Input.ToString();
					Input.Clear();
					ConsoleManager.StandardOutStream.WriteLine();
					HostConsole._current.Send_ServerMessage(text);
					ClearInputLine();
					DrawInputPrompt();
					break;
				}
				case ConsoleKey.Backspace:
					if (Input.Length > 0)
					{
						Input.Length--;
						ClearInputLine();
						DrawInputPrompt();
					}
					break;
				default:
					if (!char.IsControl(consoleKeyInfo.KeyChar))
					{
						Input.Append(consoleKeyInfo.KeyChar);
						ConsoleManager.StandardOutStream.Write(consoleKeyInfo.KeyChar);
					}
					break;
				}
			}
		}
	}
	[BepInPlugin("FleesDedicatedServer", "DedicatedServer", "0.1.0")]
	[BepInProcess("ATLYSS.exe")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static ServerHandler Handler;

		internal static ConsoleListenerWithInput ConsoleListener;

		private Plugin()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
		}

		private void GenerateLaunchScripts()
		{
			string directoryName = Path.GetDirectoryName(Paths.ExecutablePath);
			File.WriteAllText(Path.Combine(directoryName, "steam_appid.txt"), "2768430");
			if (WineDetect.IsRunningInWine)
			{
				string environmentVariable = Environment.GetEnvironmentVariable("STEAM_COMPAT_DATA_PATH");
				string environmentVariable2 = Environment.GetEnvironmentVariable("STEAM_COMPAT_CLIENT_INSTALL_PATH");
				string text = Environment.GetEnvironmentVariable("STEAM_COMPAT_TOOL_PATHS")?.Split(":").FirstOrDefault((string x) => x.Contains("/Proton"));
				string text2 = Paths.ExecutablePath.Replace('\\', '/');
				if (text2.IndexOf(':') != -1)
				{
					text2 = text2.Substring(text2.IndexOf(':') + 1);
				}
				if (string.IsNullOrWhiteSpace(environmentVariable) || string.IsNullOrWhiteSpace(environmentVariable2) || string.IsNullOrWhiteSpace(text))
				{
					Logger.LogWarning((object)"Couldn't generate a dedicated server launch script for Linux w/ Proton!");
					return;
				}
				File.WriteAllText(Path.Combine(directoryName, "start_dedicated_server.sh"), "export STEAM_COMPAT_DATA_PATH=" + environmentVariable + "\nexport STEAM_COMPAT_CLIENT_INSTALL_PATH=" + environmentVariable2 + "\n" + string.Join(" ", "\"" + text + "/proton\"", "run", "\"" + text2 + "\"", "-batchmode", "-nographics", "-server", "--doorstop-enabled", "true", "--doorstop-target-assembly", "\"" + Path.Combine(Paths.BepInExAssemblyDirectory, "BepInEx.Preloader.dll") + "\"", "&"));
			}
			else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				File.WriteAllText(Path.Combine(directoryName, "start_dedicated_server.cmd"), string.Join(" ", "start", "\"Launch Dedicated Server\"", "\"" + Paths.ExecutablePath + "\"", "-batchmode", "-nographics", "-server", "--doorstop-enabled", "true", "--doorstop-target-assembly", "\"" + Path.Combine(Paths.BepInExAssemblyDirectory, "BepInEx.Preloader.dll") + "\""));
			}
			else
			{
				Logger.LogWarning((object)"Couldn't generate a dedicated server launch script for current platform!");
			}
		}

		private void Awake()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			PluginConfig.Configure(((BaseUnityPlugin)this).Config, Environment.GetCommandLineArgs());
			GenerateLaunchScripts();
			if (!PluginConfig.IsActive)
			{
				Logger.LogInfo((object)"Argument \"-server\" not found; starting in normal mode.");
				return;
			}
			Logger.LogInfo((object)"Starting in dedicated server mode.");
			Logger.LogInfo((object)("Headless mode is " + (PluginConfig.IsHeadless ? "on" : "off") + "."));
			GameObject val = new GameObject("DedicatedServer");
			Object.DontDestroyOnLoad((Object)(object)val);
			Handler = val.AddComponent<ServerHandler>();
			Console.CursorVisible = true;
			try
			{
				ICollection<ILogListener> listeners = Logger.Listeners;
				ILogListener? obj = ((IEnumerable<ILogListener>)listeners).FirstOrDefault((Func<ILogListener, bool>)((ILogListener l) => ((object)l).GetType() == typeof(ConsoleLogListener)));
				ConsoleLogListener val2 = (ConsoleLogListener)(object)((obj is ConsoleLogListener) ? obj : null);
				if (val2 != null)
				{
					listeners.Remove((ILogListener)(object)val2);
					Logger.Listeners.Add((ILogListener)(object)(ConsoleListener = new ConsoleListenerWithInput(val2)));
					Logger.LogInfo((object)"Successfully detached default BepInEx console listener.");
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"An error occurred while accessing the console stream: {arg}");
			}
			if (PluginConfig.IsHeadless)
			{
				AudioListener.volume = 0f;
				PreventAudioChanges.LockAudio = true;
			}
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		}
	}
	internal static class PluginConfig
	{
		internal static string ServerName { get; private set; }

		internal static SteamLobbyType ServerType { get; private set; }

		internal static AtlyssSteamLobbyTag ServerTag { get; private set; }

		internal static string ServerPassword { get; private set; }

		internal static string ServerMOTD { get; private set; }

		internal static int ServerMaxPlayers { get; private set; }

		internal static int CharacterSlotToUse { get; private set; }

		internal static bool PeriodicRestartEnabled { get; private set; }

		internal static string PeriodicRestartInterval { get; private set; }

		internal static int PeriodicRestartPlayerThresholdPercent { get; private set; }

		internal static bool IsActive { get; private set; }

		internal static bool IsHeadless { get; private set; }

		internal static int PeriodicRestartIntervalSeconds { get; private set; }

		internal static int PeriodicRestartPlayerThreshold { get; private set; }

		public static void Configure(ConfigFile file, string[] args)
		{
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			IsActive = args.Contains("-server");
			IsHeadless = IsActive && Application.isBatchMode && args.Contains("-nographics");
			ServerName = file.Bind<string>("ServerData", "ServerName", "ATLYSS Server", "Name to set when launching the server.").Value;
			if (TryGetStringArgment(args, "-pve", out string value))
			{
				ServerName = value;
			}
			if (ServerName.Length > 20)
			{
				Plugin.Logger.LogWarning((object)$"Server name \"{ServerName}\" is too long ({ServerName.Length}/20). Defaulting to \"ATLYSS Server\".");
				ServerName = "ATLYSS Server";
			}
			if (string.IsNullOrWhiteSpace(ServerName))
			{
				Plugin.Logger.LogWarning((object)("Server name \"" + ServerName + "\" is invalid. Defaulting to \"ATLYSS Server\"."));
				ServerName = "ATLYSS Server";
			}
			ServerType = file.Bind<SteamLobbyType>("ServerData", "ServerType", SteamLobbyType.PUBLIC, "Type to set when launching the server.").Value;
			if (args.Contains("-public"))
			{
				ServerType = SteamLobbyType.PUBLIC;
			}
			else if (args.Contains("-friends"))
			{
				ServerType = SteamLobbyType.FRIENDS;
			}
			else if (args.Contains("-private"))
			{
				ServerType = SteamLobbyType.PRIVATE;
			}
			ServerTag = file.Bind<AtlyssSteamLobbyTag>("ServerData", "ServerTag", (AtlyssSteamLobbyTag)0, "Lobby tag to set when launching the server.").Value;
			if (args.Contains("-pve"))
			{
				ServerTag = (AtlyssSteamLobbyTag)0;
			}
			else if (args.Contains("-pvp"))
			{
				ServerTag = (AtlyssSteamLobbyTag)1;
			}
			else if (args.Contains("-social"))
			{
				ServerTag = (AtlyssSteamLobbyTag)2;
			}
			else if (args.Contains("-rp"))
			{
				ServerTag = (AtlyssSteamLobbyTag)3;
			}
			ServerPassword = file.Bind<string>("ServerData", "ServerPassword", "", "Password to use for connecting to the server. Leave empty for a passwordless server.").Value;
			if (TryGetStringArgment(args, "-password", out string value2))
			{
				ServerPassword = value2;
			}
			ServerMOTD = file.Bind<string>("ServerData", "ServerMOTD", "Welcome to ATLYSS Server!", "MOTD to display for clients that connect to the server.").Value;
			if (TryGetStringArgment(args, "-motd", out string value3))
			{
				ServerMOTD = value3;
			}
			ServerMaxPlayers = file.Bind<int>("ServerData", "ServerMaxPlayers", 16, "Maximum number of players that can connect to the server (this includes the dummy host player).").Value;
			if (TryGetIntegerArgment(args, "-maxplayers", out var value4))
			{
				ServerMaxPlayers = value4;
			}
			if (ServerMaxPlayers < 2 || ServerMaxPlayers > 250)
			{
				ServerMaxPlayers = 16;
				Plugin.Logger.LogWarning((object)"MaxPlayers must be between 2 and 250. Defaulting to 16.");
			}
			CharacterSlotToUse = file.Bind<int>("ServerData", "CharacterSlotToUse", 0, "Character slot to use for the (idle) host character. If the slot is empty, the first non-empty save slot will be used instead. If all slots are empty, a new randomized character will be created in the first slot.").Value;
			if (TryGetIntegerArgment(args, "-hostsave", out var value5))
			{
				CharacterSlotToUse = value5;
			}
			PeriodicRestartEnabled = file.Bind<bool>("ServerHealth", "PeriodicRestartEnabled", false, "Enable restarting the server automatically after the given time interval and number of players left.").Value;
			if (args.Contains("-autorestart"))
			{
				PeriodicRestartEnabled = true;
			}
			PeriodicRestartInterval = file.Bind<string>("ServerHealth", "PeriodicRestartInterval", "6h", "How much time to wait before restarting. Must be at least 30 minutes. Format is specified in days, hours, minutes and/or seconds like 1d12h30m30s, 4h45m, 4h, 30m, 1d, etc.").Value;
			if (TryGetStringArgment(args, "-autorestartin", out string value6))
			{
				PeriodicRestartInterval = value6;
			}
			if (Utils.TryParseInterval(PeriodicRestartInterval, out var seconds))
			{
				if (seconds < 1800)
				{
					seconds = 1800;
					Plugin.Logger.LogWarning((object)"Specified restart time is too low (<30m). Increasing it to 30 minutes.");
				}
				PeriodicRestartIntervalSeconds = seconds;
			}
			else
			{
				PeriodicRestartIntervalSeconds = 14400;
				Plugin.Logger.LogWarning((object)"Specified restart time is invalid. Defaulting to 4 hours.");
			}
			PeriodicRestartPlayerThresholdPercent = file.Bind<int>("ServerHealth", "PeriodicRestartPlayerThresholdPercent", 100, "Theshold of players (% of MaxPlayers) with which the server will proceed with a periodic restart. If there are more players than that, the restart timer will be stalled at 1 minute left until enough players leave. Set to 100 to ignore player counts and always restart the server.").Value;
			if (TryGetIntegerArgment(args, "-autorestartthreshold", out var value7))
			{
				PeriodicRestartPlayerThresholdPercent = value7;
			}
			if (0 > PeriodicRestartPlayerThresholdPercent || PeriodicRestartPlayerThresholdPercent > 100)
			{
				PeriodicRestartPlayerThresholdPercent = 100;
				Plugin.Logger.LogWarning((object)"Specified restart player threshold is invalid. Defaulting to 100 (threshold disabled).");
			}
			PeriodicRestartPlayerThreshold = Math.Clamp((int)Math.Ceiling((float)(ServerMaxPlayers * PeriodicRestartPlayerThresholdPercent) / 100f), 1, ServerMaxPlayers);
			file.Save();
		}

		private static bool TryGetStringArgment(string[] args, string key, [NotNullWhen(true)] out string? value)
		{
			int num = Array.IndexOf(args, key);
			value = ((num >= 0 && num + 1 < args.Length) ? args[num + 1] : null);
			return value != null;
		}

		private static bool TryGetIntegerArgment(string[] args, string key, out int value)
		{
			int num = Array.IndexOf(args, key);
			string text = ((num >= 0 && num + 1 < args.Length) ? args[num + 1] : null);
			if (text == null)
			{
				value = 0;
				return false;
			}
			if (int.TryParse((num >= 0 && num + 1 < args.Length) ? args[num + 1] : null, out value))
			{
				return true;
			}
			Plugin.Logger.LogWarning((object)("Value \"" + text + "\" supplied to argument \"" + key + "\" is not a valid integer."));
			return false;
		}
	}
	public class ServerHandler : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <ServerHeartbeat>d__10 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ServerHandler <>4__this;

			private WaitForSeconds <oneSecond>5__1;

			private bool <supressTimer>5__2;

			private bool <shouldPrintMessage>5__3;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ServerHeartbeat>d__10(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<oneSecond>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<oneSecond>5__1 = new WaitForSeconds(1f);
					break;
				case 1:
					<>1__state = -1;
					if (Object.op_Implicit((Object)(object)Player._mainPlayer) && !Player._mainPlayer._pVisual._forceHidden)
					{
						Player._mainPlayer._pVisual.Network_forceHidden = true;
					}
					if (Object.op_Implicit((Object)(object)MainMenuManager._current) && !NetworkServer.active && !NetworkClient.active)
					{
						<>4__this.StartServer();
						<>2__current = <oneSecond>5__1;
						<>1__state = 2;
						return true;
					}
					if (NetworkServer.active && PluginConfig.PeriodicRestartEnabled && <>4__this.ShutdownSecondsLeft == 0)
					{
						<>4__this.ScheduleAutomaticRestart();
					}
					else
					{
						if (!NetworkServer.active || <>4__this.ShutdownSecondsLeft <= 0)
						{
							break;
						}
						<supressTimer>5__2 = false;
						if (!<>4__this.IsAutomaticRestart || <>4__this.ShutdownSecondsLeft > 60 || <>4__this.PlayerThresholdReached)
						{
							<>4__this.ShutdownSecondsLeft--;
						}
						if (<>4__this.IsAutomaticRestart && <>4__this.ShutdownSecondsLeft <= 60 && !<>4__this.PlayerThresholdReached)
						{
							if (NetworkServer.connections.Count <= PluginConfig.PeriodicRestartPlayerThreshold)
							{
								<>4__this.PlayerThresholdReached = true;
							}
							else
							{
								<supressTimer>5__2 = true;
								if (!<>4__this.PlayerThresholdNotified)
								{
									HostConsole._current.Init_ServerMessage($"Server will restart once there are {PluginConfig.PeriodicRestartPlayerThreshold} or less players remaining.");
									<>4__this.PlayerThresholdNotified = true;
								}
							}
						}
						if (<supressTimer>5__2)
						{
							break;
						}
						if (<>4__this.ShutdownSecondsLeft == 0)
						{
							<>4__this.StopServer();
							if (!<>4__this.ShouldRestart)
							{
								<>2__current = <oneSecond>5__1;
								<>1__state = 3;
								return true;
							}
							break;
						}
						<shouldPrintMessage>5__3 = <>4__this.ShutdownSecondsLeft <= 5 || (<>4__this.ShutdownSecondsLeft <= 60 && <>4__this.ShutdownSecondsLeft % 15 == 0) || (<>4__this.ShutdownSecondsLeft <= 300 && <>4__this.ShutdownSecondsLeft % 60 == 0) || (<>4__this.ShutdownSecondsLeft <= 900 && <>4__this.ShutdownSecondsLeft % 300 == 0) || (<>4__this.ShutdownSecondsLeft <= 3600 && <>4__this.ShutdownSecondsLeft % 900 == 0);
						if (<shouldPrintMessage>5__3)
						{
							HostConsole._current.Init_ServerMessage("Server will " + (<>4__this.ShouldRestart ? "restart" : "shut down") + " in " + Utils.FormatInterval(<>4__this.ShutdownSecondsLeft) + ".");
						}
					}
					break;
				case 2:
					<>1__state = -1;
					break;
				case 3:
					<>1__state = -1;
					Application.Quit();
					return false;
				}
				<>2__current = <oneSecond>5__1;
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private int ShutdownSecondsLeft;

		private bool PlayerThresholdNotified;

		private bool PlayerThresholdReached;

		private bool ShouldRestart;

		private bool IsAutomaticRestart;

		private void Awake()
		{
			((MonoBehaviour)this).StartCoroutine(ServerHeartbeat());
		}

		public void ScheduleShutdown(int seconds)
		{
			ShutdownSecondsLeft = seconds;
			PlayerThresholdNotified = false;
			PlayerThresholdReached = false;
			IsAutomaticRestart = false;
			ShouldRestart = false;
			HostConsole._current.Init_ServerMessage($"Server will shut down in {ShutdownSecondsLeft} seconds.");
		}

		public void ScheduleRestart(int seconds)
		{
			ShutdownSecondsLeft = seconds;
			PlayerThresholdNotified = false;
			PlayerThresholdReached = false;
			IsAutomaticRestart = false;
			ShouldRestart = true;
			HostConsole._current.Init_ServerMessage($"Server will restart in {ShutdownSecondsLeft} seconds.");
		}

		public void ScheduleAutomaticRestart()
		{
			ShutdownSecondsLeft = PluginConfig.PeriodicRestartIntervalSeconds;
			PlayerThresholdNotified = false;
			PlayerThresholdReached = false;
			IsAutomaticRestart = true;
			ShouldRestart = true;
			Plugin.Logger.LogInfo((object)("Scheduled automatic restart in " + Utils.FormatInterval(PluginConfig.PeriodicRestartIntervalSeconds) + "."));
		}

		public void CancelShutdownOrRestart()
		{
			if (ShutdownSecondsLeft == 0)
			{
				Plugin.Logger.LogInfo((object)"No pending shutdown or restart to cancel.");
				return;
			}
			HostConsole._current.Init_ServerMessage((ShouldRestart ? "Restart" : "Shutdown") + " cancelled.");
			ShutdownSecondsLeft = 0;
			ShouldRestart = false;
		}

		[IteratorStateMachine(typeof(<ServerHeartbeat>d__10))]
		private IEnumerator ServerHeartbeat()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ServerHeartbeat>d__10(0)
			{
				<>4__this = this
			};
		}

		internal void StopServer()
		{
			if (NetworkServer.active)
			{
				Plugin.Logger.LogInfo((object)"Stopping Server");
				if (NetworkClient.isConnected)
				{
					((NetworkManager)AtlyssNetworkManager._current).StopHost();
				}
				else
				{
					((NetworkManager)AtlyssNetworkManager._current).StopServer();
				}
			}
		}

		internal void StartServer()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)"Starting Server");
			LogServerConfig();
			ServerHostSettings_Profile hostSettingsProfile = ProfileDataManager._current._hostSettingsProfile;
			AtlyssNetworkManager current = AtlyssNetworkManager._current;
			ProfileDataManager current2 = ProfileDataManager._current;
			LobbyListManager current3 = LobbyListManager._current;
			current._steamworksMode = true;
			current._soloMode = false;
			current._serverMode = false;
			current._serverName = PluginConfig.ServerName;
			current._serverPassword = PluginConfig.ServerPassword;
			current._sentPassword = PluginConfig.ServerPassword;
			current._serverMotd = PluginConfig.ServerMOTD;
			((NetworkManager)current).maxConnections = PluginConfig.ServerMaxPlayers;
			current3._lobbyPasswordInput.text = PluginConfig.ServerPassword;
			current3._lobbyTypeDropdown.value = (int)PluginConfig.ServerType;
			current3._hostLobbyRealm = PluginConfig.ServerTag;
			current._bannedClientList.Clear();
			current._mutedClientList.Clear();
			if (hostSettingsProfile._banList != null)
			{
				current._bannedClientList.AddRange(hostSettingsProfile._banList);
			}
			if (hostSettingsProfile._mutedList != null)
			{
				current._mutedClientList.AddRange(hostSettingsProfile._mutedList);
			}
			SteamLobbyType serverType = PluginConfig.ServerType;
			if (1 == 0)
			{
			}
			ELobbyType val = (ELobbyType)(serverType switch
			{
				SteamLobbyType.PUBLIC => 2, 
				SteamLobbyType.FRIENDS => 1, 
				SteamLobbyType.PRIVATE => 0, 
				_ => 2, 
			});
			if (1 == 0)
			{
			}
			ELobbyType val2 = val;
			int num = PluginConfig.CharacterSlotToUse;
			if (0 > num || num > current2._characterFiles.Length)
			{
				Plugin.Logger.LogWarning((object)$"Save slot {num} is invalid! Valid slots are between 0 and {current2._characterFiles.Length}. Defaulting to 0.");
				num = 0;
			}
			if (current2._characterFiles[num]._isEmptySlot)
			{
				Plugin.Logger.LogWarning((object)$"Slot {num} is empty! Creating a new character in it...");
				current2._characterFile = current2._characterFiles[num];
				CharacterCreationManager characterCreationManager = ProfileDataManager._current._charSelectManager._characterCreationManager;
				characterCreationManager.SelectRace(0);
				characterCreationManager.RandomizeAll();
				characterCreationManager._characterNameInputField.text = "Hostman";
				characterCreationManager.Create_Character();
			}
			current2._characterFile = current2._characterFiles[num];
			SteamLobby._current.HostLobby(val2);
			MainMenuManager._current._characterSelectManager.Send_CharacterFile();
		}

		private void LogServerConfig()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			bool periodicRestartEnabled = PluginConfig.PeriodicRestartEnabled;
			int periodicRestartIntervalSeconds = PluginConfig.PeriodicRestartIntervalSeconds;
			Action<object> action = Plugin.Logger.LogInfo;
			int periodicRestartPlayerThresholdPercent = PluginConfig.PeriodicRestartPlayerThresholdPercent;
			int periodicRestartPlayerThreshold = PluginConfig.PeriodicRestartPlayerThreshold;
			action("=== Server Configuration ===");
			action("Server Name     : " + PluginConfig.ServerName);
			action($"Server Type     : {PluginConfig.ServerType}");
			action($"Server Tag      : {PluginConfig.ServerTag}");
			action("Server Password : " + (string.IsNullOrEmpty(PluginConfig.ServerPassword) ? "[None]" : PluginConfig.ServerPassword));
			action("Server MOTD     : " + (string.IsNullOrEmpty(PluginConfig.ServerMOTD) ? "[None]" : PluginConfig.ServerMOTD));
			action($"Max Players     : {PluginConfig.ServerMaxPlayers}");
			action("Auto Restart    : " + ((!periodicRestartEnabled) ? "disabled" : (Utils.FormatInterval(periodicRestartIntervalSeconds) + ((periodicRestartPlayerThresholdPercent == 100) ? "" : $" at {periodicRestartPlayerThreshold} players left ({periodicRestartPlayerThresholdPercent}%)"))));
			action("============================");
		}
	}
	public enum SteamLobbyType : byte
	{
		PUBLIC,
		FRIENDS,
		PRIVATE
	}
	public static class Utils
	{
		public static bool TryParseInterval(string text, out int seconds)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				seconds = 0;
				return false;
			}
			Match match = Regex.Match(text, "^\\s*(?:([0-9]+)d)?\\s*(?:([0-9]+)h)?\\s*(?:([0-9]+)m)?\\s*(?:([0-9]+)s)?\\s*$");
			bool success = match.Groups[1].Success;
			bool success2 = match.Groups[2].Success;
			bool success3 = match.Groups[3].Success;
			bool success4 = match.Groups[4].Success;
			seconds = 0;
			if (!match.Success || !(success || success2 || success3 || success4))
			{
				return false;
			}
			seconds += (success ? (int.Parse(match.Groups[1].Value) * 86400) : 0);
			seconds += (success2 ? (int.Parse(match.Groups[2].Value) * 3600) : 0);
			seconds += (success3 ? (int.Parse(match.Groups[3].Value) * 60) : 0);
			seconds += (success4 ? int.Parse(match.Groups[4].Value) : 0);
			return true;
		}

		public static string FormatInterval(int seconds, bool pretty = false)
		{
			if (seconds == 0)
			{
				return pretty ? "0 second(s)" : "0s";
			}
			int num = seconds / 86400;
			seconds %= 86400;
			int num2 = seconds / 3600;
			seconds %= 3600;
			int num3 = seconds / 60;
			seconds %= 60;
			string text = "";
			if (num != 0)
			{
				text += (pretty ? $"{num} day(s)" : $"{num}d");
			}
			if (pretty && num != 0 && num2 != 0)
			{
				text += ", ";
			}
			if (num2 != 0)
			{
				text += (pretty ? $"{num2} hour(s)" : $"{num2}h");
			}
			if (pretty && num2 != 0 && num3 != 0)
			{
				text += ", ";
			}
			if (num3 != 0)
			{
				text += (pretty ? $"{num3} minute(s)" : $"{num3}m");
			}
			if (pretty && num3 != 0 && seconds != 0)
			{
				text += ", ";
			}
			if (seconds != 0)
			{
				text += (pretty ? $"{seconds} second(s)" : $"{seconds}s");
			}
			return text;
		}
	}
	public static class WineDetect
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		private delegate IntPtr WineGetVersion();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
		private delegate void WineGetHostVersion(out string sysName, out string version);

		private static bool _checkDone = false;

		private static bool _isRunningInWine = false;

		private static string _systemName = "";

		private static string _systemVersion = "";

		private static string _wineVersion = "";

		public static bool IsRunningInWine
		{
			get
			{
				Check();
				return _isRunningInWine;
			}
		}

		public static string SystemName
		{
			get
			{
				Check();
				return _systemName;
			}
		}

		public static string SystemVersion
		{
			get
			{
				Check();
				return _systemVersion;
			}
		}

		public static string WineVersion
		{
			get
			{
				Check();
				return _wineVersion;
			}
		}

		[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
		public static extern IntPtr GetModuleHandleA(string moduleName);

		[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
		public static extern IntPtr GetProcAddress(IntPtr module, string procName);

		public static void Check()
		{
			if (_checkDone)
			{
				return;
			}
			_checkDone = true;
			IntPtr moduleHandleA = GetModuleHandleA("ntdll.dll");
			if (!(moduleHandleA == IntPtr.Zero))
			{
				IntPtr procAddress = GetProcAddress(moduleHandleA, "wine_get_version");
				IntPtr procAddress2 = GetProcAddress(moduleHandleA, "wine_get_host_version");
				if (!(procAddress == IntPtr.Zero) && !(procAddress2 == IntPtr.Zero))
				{
					IntPtr ptr = Marshal.GetDelegateForFunctionPointer<WineGetVersion>(procAddress)();
					string wineVersion = Marshal.PtrToStringAnsi(ptr);
					Marshal.GetDelegateForFunctionPointer<WineGetHostVersion>(procAddress2)(out var sysName, out var version);
					_isRunningInWine = true;
					_systemName = sysName;
					_systemVersion = version;
					_wineVersion = wineVersion;
				}
			}
		}
	}
	internal static class ModInfo
	{
		public const string GUID = "FleesDedicatedServer";

		public const string NAME = "DedicatedServer";

		public const string VERSION = "0.1.0";
	}
}
namespace AtlyssDedicatedServer.HarmonyPatches
{
	[HarmonyPatch(typeof(HostConsole), "Send_ServerMessage")]
	[HarmonyPriority(800)]
	internal static class ChatManagerPatch
	{
		private static bool Prefix(ref string _message)
		{
			if (string.IsNullOrWhiteSpace(_message))
			{
				return false;
			}
			string[] array = _message.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			if (_message.Contains('<') || _message.Contains('>'))
			{
				Plugin.Logger.LogInfo((object)"'<' and '>' are not allowed in server messages.");
				return false;
			}
			if (_message[0] != '/')
			{
				HostConsole._current.Init_ServerMessage(_message);
				return false;
			}
			if (array[0].Trim() == "/restart")
			{
				if (array.Length < 2 || !Utils.TryParseInterval(array[1], out var seconds) || seconds < 1)
				{
					seconds = 20;
				}
				Plugin.Handler.ScheduleRestart(seconds);
				return false;
			}
			if (array[0].Trim() == "/shutdown")
			{
				if (array.Length < 2 || !Utils.TryParseInterval(array[1], out var seconds2) || seconds2 < 1)
				{
					seconds2 = 20;
				}
				Plugin.Handler.ScheduleShutdown(seconds2);
				return false;
			}
			if (_message.Trim() == "/cancelsd" || _message.Trim() == "/cancelrt")
			{
				Plugin.Handler.CancelShutdownOrRestart();
				return false;
			}
			HostConsole._current._cmdManager.Init_ConsoleCommand(array[0].TrimStart('/'), (array.Length > 1) ? array[1] : string.Empty);
			return false;
		}
	}
	[HarmonyPatch(typeof(ChatBehaviour), "New_ChatMessage")]
	internal static class ChatMirrorPatch
	{
		private static void Postfix(string _message)
		{
			string text = StripUnityRichText(_message);
			if (!(text == HostConsoleFixPatch.LastServerMessage) || !((DateTime.UtcNow - HostConsoleFixPatch.LastServerMessageTime).TotalMilliseconds < 100.0))
			{
				Logger.LogInfo((object)("[Chat] " + text));
			}
		}

		private static string StripUnityRichText(string input)
		{
			return Regex.Replace(input, "<.*?>", string.Empty);
		}
	}
	[HarmonyPatch(typeof(AtlyssNetworkManager), "Clear_ConsoleInit")]
	internal static class ClearPatch
	{
		private static bool Prefix()
		{
			return false;
		}
	}
	[HarmonyPatch(typeof(AtlyssNetworkManager), "Console_GetInput")]
	internal static class ConsolePatch
	{
		private static bool Prefix()
		{
			Plugin.ConsoleListener.ProcessInput();
			return false;
		}
	}
	[HarmonyPatch(typeof(HostConsole), "New_LogMessage")]
	internal class HostConsoleFixPatch
	{
		internal static string LastServerMessage { get; private set; } = "";


		internal static DateTime LastServerMessageTime { get; private set; }

		private static bool Prefix(string _message)
		{
			string value = $"[{DateTime.Now.Hour}:{DateTime.Now.Minute}] " + _message;
			LastServerMessage = _message;
			LastServerMessageTime = DateTime.UtcNow;
			Console.ForegroundColor = ConsoleColor.Yellow;
			Console.WriteLine(value);
			Console.ResetColor();
			return false;
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PreventAudioChanges
	{
		internal static bool LockAudio { get; set; }

		private static bool Prefix()
		{
			return !LockAudio;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}