CelesteNet.Server
Program.cs
#define INMODDIR
using Celeste.Mod.CelesteNet.DataTypes;
using Mono.Options;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Celeste.Mod.CelesteNet.Server {
public static clast Program {
public static CelesteNetServer? Server;
private static void LogHeader(TextWriter w) {
w.WriteLine("CelesteNet.Server");
w.WriteLine($"Server v.{typeof(CelesteNetServer).astembly.GetName().Version}");
w.WriteLine($"Shared v.{typeof(Logger).astembly.GetName().Version}");
w.WriteLine();
}
private static ResolveEventHandler GetastemblyResolverForDirectory(string dir) => (asmSender, asmArgs) => {
if (asmArgs.Name == null)
return null;
string name = new astemblyName(asmArgs.Name).Name ?? asmArgs.Name;
string path = Path.Combine(dir, name + ".dll");
if (!File.Exists(path))
path = Path.Combine(dir, name + ".exe");
return File.Exists(path) ? astembly.LoadFrom(path) : null;
};
public static void Main(string[] args) {
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
string? serverPath = typeof(CelesteNetServer).astembly.Location;
if (!serverPath.IsNullOrEmpty() && !(serverPath = Path.GetDirectoryName(Path.GetFullPath(serverPath))).IsNullOrEmpty())
AppDomain.CurrentDomain.astemblyResolve += GetastemblyResolverForDirectory(serverPath);
#if INMODDIR && NETFRAMEWORK
string? celestePath = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "..", ".."));
if (!File.Exists(Path.Combine(celestePath, "Celeste.exe"))) {
celestePath = null;
} else {
AppDomain.CurrentDomain.astemblyResolve += GetastemblyResolverForDirectory(celestePath);
}
#endif
MainMain(args);
}
private static void MainMain(string[] args) {
LogHeader(Console.Out);
Thread.CurrentThread.Name = "Main Thread";
CelesteNetServerSettings settings = new();
settings.Load();
settings.Save();
bool showHelp = false;
string? logFile = "log-celestenet.txt";
OptionSet options = new() {
{
"v|loglevel:",
$"Change the log level, ranging from {LogLevel.CRI} ({(int) LogLevel.CRI}) to {LogLevel.DEV} ({(int) LogLevel.DEV}). Defaults to {LogLevel.INF} ({(int) LogLevel.INF}).",
v => {
if (Enum.TryParse(v, true, out LogLevel level)) {
Logger.Level = level;
} else {
Logger.Level--;
}
if (Logger.Level < LogLevel.DEV)
Logger.Level = LogLevel.DEV;
if (Logger.Level > LogLevel.CRI)
Logger.Level = LogLevel.CRI;
Console.WriteLine($"Log level changed to {Logger.Level}");
}
},
{ "log", "Specify the file to log to.", v => { if (v != null) logFile = v; } },
{ "nolog", "Disable logging to a file.", v => { if (v != null) logFile = null; } },
{ "h|help", "Show this message and exit.", v => showHelp = v != null },
};
try {
options.Parse(args);
} catch (OptionException e) {
Console.WriteLine(e.Message);
Console.WriteLine("Use --help for argument info.");
return;
}
if (showHelp) {
options.WriteOptionDescriptions(Console.Out);
return;
}
if (logFile == null) {
MainRun(settings);
return;
}
if (File.Exists(logFile))
File.Delete(logFile);
using FileStream fileStream = new(logFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
using StreamWriter fileWriter = new(fileStream, Console.OutputEncoding);
using LogWriter logWriter = new() {
STDOUT = Console.Out,
File = fileWriter
};
LogHeader(fileWriter);
try {
Console.SetOut(logWriter);
MainRun(settings);
} finally {
if (logWriter.STDOUT != null) {
Console.SetOut(logWriter.STDOUT);
logWriter.STDOUT = null;
}
}
}
private static void MainRun(CelesteNetServerSettings settings) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
using CelesteNetServer server = Server = new(settings);
server.Start();
server.Wait();
Server = null;
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) {
if (e.IsTerminating) {
_CriticalFailureIsUnhandledException = true;
CriticalFailureHandler(e.ExceptionObject as Exception ?? new Exception("Unknown unhandled exception."));
} else {
Logger.Log(LogLevel.CRI, "main", "Encountered an UNHANDLED EXCEPTION. Server shutting down.");
Logger.LogDetailedException(e.ExceptionObject as Exception ?? new Exception("Unknown unhandled exception."));
}
}
private static bool _CriticalFailureIsUnhandledException;
public static void CriticalFailureHandler(Exception e) {
Logger.Log(LogLevel.CRI, "main", "Encountered a CRITICAL FAILURE. Server shutting down.");
Logger.LogDetailedException(e ?? new Exception("Unknown exception."));
if (!_CriticalFailureIsUnhandledException)
Environment.Exit(-1);
}
}
}