diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/EffectPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/EffectPacketTests.cs index 3e938bc44..9766c677e 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/EffectPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/EffectPacketTests.cs @@ -85,7 +85,6 @@ namespace Server.Tests.Network.Packets public void TestHuedEffect() { var effectType = EffectType.Moving; - Serial serial = 0x4000; Serial from = 0x1000; Serial to = 0x2000; var itemId = 0x100; diff --git a/Projects/Server/AssemblyHandler.cs b/Projects/Server/AssemblyHandler.cs index 9c0810621..daf4e8b89 100644 --- a/Projects/Server/AssemblyHandler.cs +++ b/Projects/Server/AssemblyHandler.cs @@ -111,9 +111,7 @@ namespace Server public static string EnsureDirectory(string dir) { var path = Path.Combine(Core.BaseDirectory, dir); - - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); + Directory.CreateDirectory(path); return path; } diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 19ba7496c..2422f5f61 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -1,22 +1,17 @@ -/*************************************************************************** - * Main.cs - * ------------------- - * begin : May 1, 2002 - * copyright : (C) The RunUO Software Team - * email : info@runuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Main.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ using System; using System.Collections.Generic; @@ -42,7 +37,6 @@ namespace Server private static bool m_Crashed; private static Thread timerThread; private static string m_BaseDirectory; - private static string m_ExePath; private static bool m_Profiling; private static DateTime m_ProfileStart; @@ -62,8 +56,6 @@ namespace Server private static readonly double m_HighFrequency = 1000.0 / Stopwatch.Frequency; private static readonly double m_LowFrequency = 1000.0 / TimeSpan.TicksPerSecond; - internal static ConsoleEventHandler m_ConsoleEventHandler; - private static int m_CycleIndex = 1; private static readonly float[] m_CyclesPerSecond = new float[100]; @@ -138,8 +130,6 @@ namespace Server public static int ProcessorCount { get; private set; } - public static string ExePath => m_ExePath ??= Assembly.Location; - public static string BaseDirectory { get @@ -148,7 +138,7 @@ namespace Server { try { - m_BaseDirectory = ExePath; + m_BaseDirectory = Assembly.Location; if (m_BaseDirectory.Length > 0) { @@ -165,7 +155,9 @@ namespace Server } } - public static bool Closing { get; private set; } + public static CancellationTokenSource ClosingTokenSource { get; } = new CancellationTokenSource(); + + public static bool Closing => ClosingTokenSource.IsCancellationRequested; public static float CyclesPerSecond => m_CyclesPerSecond[(m_CycleIndex - 1) % m_CyclesPerSecond.Length]; @@ -279,15 +271,6 @@ namespace Server if (!close) { - try - { - // Close all listeners - } - catch - { - // ignored - } - Console.WriteLine("This exception is fatal, press return to exit"); Console.ReadLine(); } @@ -296,30 +279,56 @@ namespace Server } } - private static bool OnConsoleEvent(ConsoleEventType type) - { - if (World.Saving || type == ConsoleEventType.CTRL_LOGOFF_EVENT) - { - return true; - } - - Kill(); // Kill -> HandleClosed will handle waiting for the completion of flushing to disk - - return true; - } - private static void CurrentDomain_ProcessExit(object sender, EventArgs e) { - HandleClosed(); + if (!Closing) + { + HandleClosed(); + } + } + + private static void Console_CancelKeyPressed(object sender, ConsoleCancelEventArgs e) + { + var keypress = e.SpecialKey switch + { + ConsoleSpecialKey.ControlBreak => "CTRL+BREAK", + _ => "CTRL+C" + }; + + Console.WriteLine("Core: Detected {0} pressed.", keypress); + e.Cancel = true; + Kill(); } public static void Kill(bool restart = false) { + if (Closing) + { + return; + } + HandleClosed(); if (restart) { - Process.Start(ExePath, Arguments); + if (IsWindows) + { + Process.Start("dotnet", Assembly.Location); + } + else + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = Assembly.Location, + UseShellExecute = true + } + }; + + process.Start(); + } } Process.Kill(); @@ -327,14 +336,9 @@ namespace Server private static void HandleClosed() { - if (Closing) - { - return; - } + ClosingTokenSource.Cancel(); - Closing = true; - - Console.Write("Exiting..."); + Console.Write("Core: Shutting down..."); World.WaitForWriteCompletion(); @@ -426,11 +430,7 @@ namespace Server Console.WriteLine("Core: Optimizing for {0} processor{1}", ProcessorCount, ProcessorCount == 1 ? "" : "s"); } - if (IsWindows) - { - m_ConsoleEventHandler = OnConsoleEvent; - UnsafeNativeMethods.SetConsoleCtrlHandler(m_ConsoleEventHandler, true); - } + Console.CancelKeyPress += Console_CancelKeyPressed; if (GCSettings.IsServerGC) { @@ -472,13 +472,14 @@ namespace Server EventSink.InvokeServerStarted(); // Start net socket server - var host = TcpServer.CreateWebHostBuilder().Build(); - var life = host.Services.GetRequiredService(); - life.ApplicationStopping.Register(() => { Kill(); }); + _tcpHost = TcpServer.CreateWebHostBuilder().Build(); - host.Run(); + // Run indefinitely and block + _tcpHost.RunAsync(ClosingTokenSource.Token).Wait(); } + private static IWebHost _tcpHost; + public static void RunEventLoop(IMessagePumpService messagePumpService) { try @@ -542,7 +543,10 @@ namespace Server { var isItem = type.IsSubclassOf(typeof(Item)); - if (!isItem && !type.IsSubclassOf(typeof(Mobile))) return; + if (!isItem && !type.IsSubclassOf(typeof(Mobile))) + { + return; + } if (isItem) { @@ -595,22 +599,5 @@ namespace Server Parallel.ForEach(assembly.GetTypes(), VerifyType); } } - - internal enum ConsoleEventType - { - CTRL_C_EVENT, - CTRL_BREAK_EVENT, - CTRL_CLOSE_EVENT, - CTRL_LOGOFF_EVENT = 5, - CTRL_SHUTDOWN_EVENT - } - - internal delegate bool ConsoleEventHandler(ConsoleEventType type); - - internal static class UnsafeNativeMethods - { - [DllImport("Kernel32")] - internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler callback, bool add); - } } } diff --git a/Projects/Server/Network/MessagePumpService.cs b/Projects/Server/Network/MessagePumpService.cs index e38744b2c..27f77ad76 100644 --- a/Projects/Server/Network/MessagePumpService.cs +++ b/Projects/Server/Network/MessagePumpService.cs @@ -40,7 +40,9 @@ namespace Server.Network while (!m_WorkQueue.IsEmpty && count++ < 250) { if (!m_WorkQueue.TryDequeue(out var work)) + { break; + } var seq = new ReadOnlySequence(work.MemoryOwner.Memory.Slice(0, work.Length)); work.OnReceive(work.State, new PacketReader(seq)); diff --git a/Projects/Server/Network/NetState.cs b/Projects/Server/Network/NetState.cs index 5793ae19f..eda972410 100644 --- a/Projects/Server/Network/NetState.cs +++ b/Projects/Server/Network/NetState.cs @@ -181,36 +181,71 @@ namespace Server.Network m_Version = value; if (value >= m_Version70610) + { ProtocolChanges = ProtocolChanges.Version70610; + } + if (value >= m_Version70500) + { ProtocolChanges = ProtocolChanges.Version70500; + } + if (value >= m_Version704565) + { ProtocolChanges = ProtocolChanges.Version704565; + } else if (value >= m_Version70331) + { ProtocolChanges = ProtocolChanges.Version70331; + } else if (value >= m_Version70300) + { ProtocolChanges = ProtocolChanges.Version70300; + } else if (value >= m_Version70160) + { ProtocolChanges = ProtocolChanges.Version70160; + } else if (value >= m_Version70130) + { ProtocolChanges = ProtocolChanges.Version70130; + } else if (value >= m_Version7090) + { ProtocolChanges = ProtocolChanges.Version7090; + } else if (value >= m_Version7000) + { ProtocolChanges = ProtocolChanges.Version7000; + } else if (value >= m_Version60142) + { ProtocolChanges = ProtocolChanges.Version60142; + } else if (value >= m_Version6017) + { ProtocolChanges = ProtocolChanges.Version6017; + } else if (value >= m_Version6000) + { ProtocolChanges = ProtocolChanges.Version6000; + } else if (value >= m_Version502b) + { ProtocolChanges = ProtocolChanges.Version502b; + } else if (value >= m_Version500a) + { ProtocolChanges = ProtocolChanges.Version500a; + } else if (value >= m_Version407a) + { ProtocolChanges = ProtocolChanges.Version407a; - else if (value >= m_Version400a) ProtocolChanges = ProtocolChanges.Version400a; + } + else if (value >= m_Version400a) + { + ProtocolChanges = ProtocolChanges.Version400a; + } } } @@ -275,7 +310,9 @@ namespace Server.Network var info = ExpansionInfo.Table[i]; if (info.RequiredClient != null && Version >= info.RequiredClient || (Flags & info.ClientFlags) != 0) + { return info; + } } return ExpansionInfo.GetInfo(Expansion.None); @@ -292,21 +329,31 @@ namespace Server.Network { for (var i = Trades.Count - 1; i >= 0; --i) { - if (i >= Trades.Count) continue; + if (i >= Trades.Count) + { + continue; + } var trade = Trades[i]; if (trade.From.Mobile.Deleted || trade.To.Mobile.Deleted || !trade.From.Mobile.Alive || !trade.To.Mobile.Alive || !trade.From.Mobile.InRange(trade.To.Mobile, 2) || - trade.From.Mobile.Map != trade.To.Mobile.Map) trade.Cancel(); + trade.From.Mobile.Map != trade.To.Mobile.Map) + { + trade.Cancel(); + } } } public void CancelAllTrades() { for (var i = Trades.Count - 1; i >= 0; --i) + { if (i < Trades.Count) + { Trades[i].Cancel(); + } + } } public void RemoveTrade(SecureTrade trade) @@ -320,7 +367,10 @@ namespace Server.Network { var trade = Trades[i]; - if (trade.From.Mobile == m || trade.To.Mobile == m) return trade; + if (trade.From.Mobile == m || trade.To.Mobile == m) + { + return trade; + } } return null; @@ -335,9 +385,15 @@ namespace Server.Network var from = trade.From; var to = trade.To; - if (from.Mobile == Mobile && to.Mobile == m) return from.Container; + if (from.Mobile == Mobile && to.Mobile == m) + { + return @from.Container; + } - if (from.Mobile == m && to.Mobile == Mobile) return to.Container; + if (from.Mobile == m && to.Mobile == Mobile) + { + return to.Container; + } } return null; @@ -521,21 +577,29 @@ namespace Server.Network while (true) { if (AsyncState.Paused) + { continue; + } var result = await inPipe.ReadAsync(); if (result.IsCanceled || result.IsCompleted) + { return; + } var seq = result.Buffer; if (seq.IsEmpty) + { break; + } var pos = PacketHandlers.ProcessPacket(messagePumpService, this, seq); if (pos <= 0) + { break; + } inPipe.AdvanceTo(seq.Slice(0, pos).End); } @@ -545,9 +609,9 @@ namespace Server.Network Console.WriteLine(ex); TraceException(ex); } - catch (Exception ex) + catch { - Console.WriteLine(ex); + // Console.WriteLine(ex); } finally { @@ -595,7 +659,9 @@ namespace Server.Network { var disposing = Interlocked.Exchange(ref m_Disposing, 1); if (disposing == 1) + { return; + } try { @@ -620,7 +686,9 @@ namespace Server.Network while (breakout++ < 200) { if (!m_Disposed.TryDequeue(out var ns)) + { break; + } var m = ns.Mobile; var a = ns.Account; @@ -639,9 +707,13 @@ namespace Server.Network ns.CityInfo = null; if (a != null) + { ns.WriteConsole("Disconnected. [{0} Online] [{1}]", TcpServer.Instances.Count, a); + } else + { ns.WriteConsole("Disconnected. [{0} Online]", TcpServer.Instances.Count); + } } } diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs index 132ad25e0..59180560b 100644 --- a/Projects/Server/Network/TcpServer.cs +++ b/Projects/Server/Network/TcpServer.cs @@ -54,15 +54,21 @@ namespace Server.Network public static IPAddress[] GetListeningAddresses(IPEndPoint ipep) { if (m_ListeningAddresses != null) + { return m_ListeningAddresses; + } var list = new List(); foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces()) { var properties = adapter.GetIPProperties(); foreach (var unicast in properties.UnicastAddresses) + { if (ipep.AddressFamily == unicast.Address.AddressFamily) + { list.Add(unicast.Address); + } + } } return list.ToArray(); @@ -71,10 +77,16 @@ namespace Server.Network private static void DisplayListener(IPEndPoint ipep) { if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) + { foreach (var ip in m_ListeningAddresses) + { Console.WriteLine("Listening: {0}:{1}", ip, ipep.Port); + } + } else + { Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port); + } } } } diff --git a/Projects/UOContent/Misc/AutoRestart.cs b/Projects/UOContent/Misc/AutoRestart.cs index ab091af6f..7c8bebe2c 100644 --- a/Projects/UOContent/Misc/AutoRestart.cs +++ b/Projects/UOContent/Misc/AutoRestart.cs @@ -24,7 +24,9 @@ namespace Server.Misc m_RestartTime = DateTime.UtcNow.Date + RestartTime; if (m_RestartTime < DateTime.UtcNow) + { m_RestartTime += TimeSpan.FromDays(1.0); + } } public static bool Restarting { get; private set; } @@ -49,12 +51,12 @@ namespace Server.Misc } } - private void Warning_Callback() + private static void Warning_Callback() { World.Broadcast(0x22, true, "The server is going down shortly."); } - private void Restart_Callback() + private static void Restart_Callback() { Core.Kill(true); } @@ -62,10 +64,14 @@ namespace Server.Misc protected override void OnTick() { if (Restarting || !Enabled) + { return; + } if (DateTime.UtcNow < m_RestartTime) + { return; + } if (WarningDelay > TimeSpan.Zero) { diff --git a/Projects/UOContent/Misc/Broadcasts.cs b/Projects/UOContent/Misc/Broadcasts.cs index d2b487e29..74eb2ad4f 100644 --- a/Projects/UOContent/Misc/Broadcasts.cs +++ b/Projects/UOContent/Misc/Broadcasts.cs @@ -22,14 +22,14 @@ namespace Server.Misc public static void EventSink_Shutdown() { - /* try - { - World.Broadcast(0x35, true, "The server has shut down."); - } - catch - { - // ignored - }*/ + try + { + World.Broadcast(0x35, true, "The server has shut down."); + } + catch + { + // ignored + } } } } diff --git a/Projects/UOContent/Misc/CrashGuard.cs b/Projects/UOContent/Misc/CrashGuard.cs index 0ac3b060d..d1d22de41 100644 --- a/Projects/UOContent/Misc/CrashGuard.cs +++ b/Projects/UOContent/Misc/CrashGuard.cs @@ -8,32 +8,38 @@ namespace Server.Misc { public static class CrashGuard { - private static readonly bool Enabled = true; - private static readonly bool SaveBackup = true; - private static readonly bool RestartServer = true; - private static readonly bool GenerateReport = true; + // TODO: Make this configurable + private const bool Enabled = true; + private const bool SaveBackup = true; + private const bool RestartServer = true; + private const bool GenerateReport = true; public static void Initialize() { - if (Enabled) // If enabled, register our crash event handler + if (Enabled) + { EventSink.ServerCrashed += CrashGuard_OnCrash; + } } public static void CrashGuard_OnCrash(ServerCrashedEventArgs e) { if (GenerateReport) + { GenerateCrashReport(e); + } World.WaitForWriteCompletion(); if (SaveBackup) + { Backup(); + } - /*if (Core.Service) - e.Close = true; - else */ if (RestartServer) + { Restart(e); + } } private static void SendEmail(string filePath) @@ -43,29 +49,13 @@ namespace Server.Misc Email.SendCrashEmail(filePath); } - private static string GetRoot() - { - try - { - return Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]); - } - catch - { - return ""; - } - } - - private static string Combine(string path1, string path2) => path1.Length == 0 ? path2 : Path.Combine(path1, path2); - private static void Restart(ServerCrashedEventArgs e) { - var root = GetRoot(); - Console.Write("Crash: Restarting..."); try { - Process.Start(Core.ExePath, Core.Arguments); + Process.Start(Core.Assembly.Location, Core.Arguments); Console.WriteLine("done"); e.Close = true; @@ -76,26 +66,20 @@ namespace Server.Misc } } - private static void CreateDirectory(string path) - { - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - } - - private static void CreateDirectory(string path1, string path2) - { - CreateDirectory(Combine(path1, path2)); - } - private static void CopyFile(string rootOrigin, string rootBackup, string path) { - var originPath = Combine(rootOrigin, path); - var backupPath = Combine(rootBackup, path); + var originPath = Path.Combine(rootOrigin, path); + if (!File.Exists(originPath)) + { + return; + } + + var backupPath = Path.Combine(rootBackup, path); + Directory.CreateDirectory(Path.GetDirectoryName(backupPath)); try { - if (File.Exists(originPath)) - File.Copy(originPath, backupPath); + File.Copy(originPath, backupPath); } catch { @@ -111,17 +95,9 @@ namespace Server.Misc { var timeStamp = GetTimeStamp(); - var root = GetRoot(); - var rootBackup = Combine(root, $"Backups/Crashed/{timeStamp}/"); - var rootOrigin = Combine(root, "Saves/"); - - // Create new directories - CreateDirectory(rootBackup); - CreateDirectory(rootBackup, "Accounts/"); - CreateDirectory(rootBackup, "Items/"); - CreateDirectory(rootBackup, "Mobiles/"); - CreateDirectory(rootBackup, "Guilds/"); - CreateDirectory(rootBackup, "Regions/"); + var root = Core.BaseDirectory; + var rootBackup = Path.Combine(root, $"Backups/Crashed/{timeStamp}/"); + var rootOrigin = Path.Combine(root, "Saves/"); // Copy files CopyFile(rootOrigin, rootBackup, "Accounts/Accounts.xml"); @@ -157,12 +133,12 @@ namespace Server.Misc var timeStamp = GetTimeStamp(); var fileName = $"Crash {timeStamp}.log"; - var root = GetRoot(); - var filePath = Combine(root, fileName); + var root = Core.BaseDirectory; + var filePath = Path.Combine(root, fileName); using (var op = new StreamWriter(filePath)) { - var ver = Core.Assembly.GetName().Version ?? new Version("0.0.0.0"); + var ver = Core.Version; op.WriteLine("Server Crash Report"); op.WriteLine("==================="); @@ -170,7 +146,7 @@ namespace Server.Misc op.WriteLine($"ModernUO Version {ver.Major}.{ver.Minor}, Build {ver.Build}.{ver.Revision}"); op.WriteLine("Operating System: {0}", Environment.OSVersion); op.WriteLine(".NET Framework: {0}", Environment.Version); - op.WriteLine("Time: {0}", DateTime.UtcNow); + op.WriteLine("Time: {0}", timeStamp); try { @@ -209,12 +185,16 @@ namespace Server.Misc op.Write("+ {0}:", state); if (state.Account is Account a) + { op.Write(" (account = {0})", a.Username); + } var m = state.Mobile; if (m != null) + { op.Write(" (mobile = 0x{0:X} '{1}')", m.Serial.Value, m.Name); + } op.WriteLine(); }