diff --git a/LibraryCore/Network/BaseConnection.cs b/LibraryCore/Network/BaseConnection.cs index 29a8dcfa..3525f642 100644 --- a/LibraryCore/Network/BaseConnection.cs +++ b/LibraryCore/Network/BaseConnection.cs @@ -22,6 +22,8 @@ public abstract class BaseConnection public bool AdditionalLogging; + public string IPAddress => Client?.Client?.RemoteEndPoint?.ToString()?.Split(':')[0]; + protected TcpClient Client; public DateTime TimeConnected { get; set; } diff --git a/Server/Helpers/JsonImporter.cs b/Server/Helpers/JsonImporter.cs index 57ca7f00..eb764a77 100644 --- a/Server/Helpers/JsonImporter.cs +++ b/Server/Helpers/JsonImporter.cs @@ -50,7 +50,7 @@ public static void Import(JsonSerializerOptions options) } catch (Exception ex) { - SEnvir.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.Message); XtraMessageBox.Show($"Failed to import all rows.\r\n\r\n{ex.Message}", "Fail", MessageBoxButtons.OK); } diff --git a/Server/SMain.cs b/Server/SMain.cs index 54946bcc..8e78ceed 100644 --- a/Server/SMain.cs +++ b/Server/SMain.cs @@ -8,6 +8,7 @@ using PluginCore; using Server.DBModels; using Server.Envir; +using Server.Infrastructure.Network.Smtp; using Server.Views; using System; using System.Collections.Generic; @@ -47,7 +48,7 @@ private void SetupPlugin() private void PluginLoader_Log(object sender, PluginCore.LogEventArgs e) { - SEnvir.Log(e.Message); + SEnvir.ServerLogger.Log(e.Message); } private void PluginLoader_ShowView(object sender, ShowViewEventArgs e) @@ -129,7 +130,7 @@ private void Application_Idle(object sender, EventArgs e) } catch (Exception ex) { - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); } } @@ -142,6 +143,7 @@ protected override void OnClosing(CancelEventArgs e) if (SEnvir.EnvirThread == null) return; + SEnvir.Started = false; while (SEnvir.EnvirThread != null) Thread.Sleep(1); @@ -187,7 +189,7 @@ private void UpdateInterface() StartServerButton.Enabled = SEnvir.EnvirThread == null; StopServerButton.Enabled = SEnvir.Started; - ConnectionLabel.Caption = string.Format(@"Connections: {0:#,##0}", SEnvir.Connections.Count); + ConnectionLabel.Caption = string.Format(@"Connections: {0:#,##0}", SEnvir.UserConnectionService.ActiveConnections.Count); ObjectLabel.Caption = string.Format(@"Objects: {0} of {1:#,##0}", SEnvir.ActiveObjects.Count, SEnvir.Objects.Count); ProcessLabel.Caption = string.Format(@"Process Count: {0:#,##0}", SEnvir.ProcessObjectCount); LoopLabel.Caption = string.Format(@"Loop Count: {0:#,##0}", SEnvir.LoopCount); @@ -200,42 +202,42 @@ private void UpdateInterface() const decimal MB = KB * 1024; const decimal GB = MB * 1024; - if (SEnvir.TotalBytesReceived > GB) - TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0.0}GB", SEnvir.TotalBytesReceived / GB); - else if (SEnvir.TotalBytesReceived > MB) - TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0.0}MB", SEnvir.TotalBytesReceived / MB); - else if (SEnvir.TotalBytesReceived > KB) - TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0}KB", SEnvir.TotalBytesReceived / KB); + if (SEnvir.UserConnectionService.TotalBytesReceived > GB) + TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0.0}GB", SEnvir.UserConnectionService.TotalBytesReceived / GB); + else if (SEnvir.UserConnectionService.TotalBytesReceived > MB) + TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0.0}MB", SEnvir.UserConnectionService.TotalBytesReceived / MB); + else if (SEnvir.UserConnectionService.TotalBytesReceived > KB) + TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0}KB", SEnvir.UserConnectionService.TotalBytesReceived / KB); else - TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0}B", SEnvir.TotalBytesReceived); - - if (SEnvir.TotalBytesSent > GB) - TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0.0}GB", SEnvir.TotalBytesSent / GB); - else if (SEnvir.TotalBytesSent > MB) - TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0.0}MB", SEnvir.TotalBytesSent / MB); - else if (SEnvir.TotalBytesSent > KB) - TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0}KB", SEnvir.TotalBytesSent / KB); + TotalDownloadLabel.Caption = string.Format(@"Downloaded: {0:#,##0}B", SEnvir.UserConnectionService.TotalBytesReceived); + + if (SEnvir.UserConnectionService.TotalBytesSent > GB) + TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0.0}GB", SEnvir.UserConnectionService.TotalBytesSent / GB); + else if (SEnvir.UserConnectionService.TotalBytesSent > MB) + TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0.0}MB", SEnvir.UserConnectionService.TotalBytesSent / MB); + else if (SEnvir.UserConnectionService.TotalBytesSent > KB) + TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0}KB", SEnvir.UserConnectionService.TotalBytesSent / KB); else - TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0}B", SEnvir.TotalBytesSent); + TotalUploadLabel.Caption = string.Format(@"Uploaded: {0:#,##0}B", SEnvir.UserConnectionService.TotalBytesSent); - if (SEnvir.DownloadSpeed > GB) - DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0.0}GBps", SEnvir.DownloadSpeed / GB); - else if (SEnvir.DownloadSpeed > MB) - DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0.0}MBps", SEnvir.DownloadSpeed / MB); - else if (SEnvir.DownloadSpeed > KB) - DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0}KBps", SEnvir.DownloadSpeed / KB); + if (SEnvir.UserConnectionService.DownloadSpeed > GB) + DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0.0}GBps", SEnvir.UserConnectionService.DownloadSpeed / GB); + else if (SEnvir.UserConnectionService.DownloadSpeed > MB) + DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0.0}MBps", SEnvir.UserConnectionService.DownloadSpeed / MB); + else if (SEnvir.UserConnectionService.DownloadSpeed > KB) + DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0}KBps", SEnvir.UserConnectionService.DownloadSpeed / KB); else - DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0}Bps", SEnvir.DownloadSpeed); - - if (SEnvir.UploadSpeed > GB) - UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0.0}GBps", SEnvir.UploadSpeed / GB); - else if (SEnvir.UploadSpeed > MB) - UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0.0}MBps", SEnvir.UploadSpeed / MB); - else if (SEnvir.UploadSpeed > KB) - UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0}KBps", SEnvir.UploadSpeed / KB); + DownloadSpeedLabel.Caption = string.Format(@"D/L Speed: {0:#,##0}Bps", SEnvir.UserConnectionService.DownloadSpeed); + + if (SEnvir.UserConnectionService.UploadSpeed > GB) + UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0.0}GBps", SEnvir.UserConnectionService.UploadSpeed / GB); + else if (SEnvir.UserConnectionService.UploadSpeed > MB) + UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0.0}MBps", SEnvir.UserConnectionService.UploadSpeed / MB); + else if (SEnvir.UserConnectionService.UploadSpeed > KB) + UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0}KBps", SEnvir.UserConnectionService.UploadSpeed / KB); else - UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0}Bps", SEnvir.UploadSpeed); + UploadSpeedLabel.Caption = string.Format(@"U/L Speed: {0:#,##0}Bps", SEnvir.UserConnectionService.UploadSpeed); } private void StartServerButton_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) @@ -248,7 +250,7 @@ private void StartServerButton_ItemClick(object sender, DevExpress.XtraBars.Item } catch (Exception ex) { - SEnvir.Log($"Exception: " + ex.ToString(), true); + SEnvir.ServerLogger.Log($"Exception: " + ex.ToString()); } } diff --git a/Server/Views/ChatLogView.cs b/Server/Views/ChatLogView.cs index 7d7edc98..ec6f4195 100644 --- a/Server/Views/ChatLogView.cs +++ b/Server/Views/ChatLogView.cs @@ -18,11 +18,11 @@ public ChatLogView() private void InterfaceTimer_Tick(object sender, EventArgs e) { - while (!SEnvir.ChatLogs.IsEmpty) + while (!SEnvir.ChatAppLogs.IsEmpty) //TODO: not sure if this was a bug or intentional? why was it using SEnvir.ChatLogs instead of ChatDisplayLogs { string log; - if (!SEnvir.DisplayChatLogs.TryDequeue(out log)) continue; + if (!SEnvir.ChatAppLogs.TryDequeue(out log)) continue; Logs.Add(log); } diff --git a/Server/Views/ConfigView.cs b/Server/Views/ConfigView.cs index 07b39322..ac439809 100644 --- a/Server/Views/ConfigView.cs +++ b/Server/Views/ConfigView.cs @@ -38,13 +38,13 @@ private void SyncronizeRemoteButton_Click(object sender, EventArgs e) private void SyncronizeLocalButton_Click(object sender, EventArgs e) { - SEnvir.Log($"Starting local syncronization..."); + SEnvir.ServerLogger.Log($"Starting local syncronization..."); SMain.Session.Save(true); File.Copy(SMain.Session.SystemPath, Path.Combine(Config.ClientPath, "Data\\", Path.GetFileName(SMain.Session.SystemPath)), true); - SEnvir.Log($"Syncronization completed..."); + SEnvir.ServerLogger.Log($"Syncronization completed..."); } protected override void OnLoad(EventArgs e) diff --git a/Server/Views/MapViewer.cs b/Server/Views/MapViewer.cs index 146f138b..a7772d10 100644 --- a/Server/Views/MapViewer.cs +++ b/Server/Views/MapViewer.cs @@ -205,7 +205,7 @@ private void RenderEnvironment() } catch (Exception ex) { - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); Manager.AttemptRecovery(); } @@ -2152,7 +2152,7 @@ public void Load(string fileName) } catch (Exception ex) { - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); } TextureValid = false; } diff --git a/Server/Views/SyncForm.cs b/Server/Views/SyncForm.cs index 5ed303e3..0d7ab03e 100644 --- a/Server/Views/SyncForm.cs +++ b/Server/Views/SyncForm.cs @@ -1,17 +1,8 @@ -using MirDB; -using Server.Envir; +using Server.Envir; +using Server.Infrastructure.Network.Http; using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace Server.Views @@ -33,7 +24,7 @@ private void btnSync_Click(object sender, EventArgs e) string key = Uri.EscapeDataString(txtKey.Text); - var url = $"{txtRemoteIP.Text}?Type={WebServer.SystemDBSyncCommand}&Key={key}"; + var url = $"{txtRemoteIP.Text}?Type={HttpWebServer.SystemDBSyncCommand}&Key={key}"; try { HttpResponseMessage response = client.PostAsync(url, content).Result; diff --git a/Server/Views/SystemLogView.cs b/Server/Views/SystemLogView.cs index c7013549..2e50a120 100644 --- a/Server/Views/SystemLogView.cs +++ b/Server/Views/SystemLogView.cs @@ -18,11 +18,11 @@ public SystemLogView() private void InterfaceTimer_Tick(object sender, EventArgs e) { - while (!SEnvir.DisplayLogs.IsEmpty) + while (!SEnvir.ServerAppLogs.IsEmpty) { string log; - if (!SEnvir.DisplayLogs.TryDequeue(out log)) continue; + if (!SEnvir.ServerAppLogs.TryDequeue(out log)) continue; Logs.Add(log); } diff --git a/ServerCore/Program.cs b/ServerCore/Program.cs index f1681abc..2ded1326 100644 --- a/ServerCore/Program.cs +++ b/ServerCore/Program.cs @@ -1,5 +1,4 @@ -using Autofac; -using Library; +using Library; using Server.Envir; using System; using System.Reflection; diff --git a/ServerLibrary/DBModels/AccountInfo.cs b/ServerLibrary/DBModels/AccountInfo.cs index 0164c24d..7d600e8c 100644 --- a/ServerLibrary/DBModels/AccountInfo.cs +++ b/ServerLibrary/DBModels/AccountInfo.cs @@ -2,6 +2,7 @@ using Library.SystemModels; using MirDB; using Server.Envir; +using Server.Infrastructure.Service.Connection; using System; using System.Collections.Generic; using System.Linq; @@ -617,7 +618,7 @@ public CharacterInfo LastCharacter public int WrongPasswordCount; - public SConnection Connection; + public UserConnection Connection; public string Key; protected override void OnCreated() diff --git a/ServerLibrary/DBModels/GuildInfo.cs b/ServerLibrary/DBModels/GuildInfo.cs index a5460b1b..b5ddc645 100644 --- a/ServerLibrary/DBModels/GuildInfo.cs +++ b/ServerLibrary/DBModels/GuildInfo.cs @@ -319,7 +319,7 @@ protected override void OnLoaded() { if (item.Slot < 0 || item.Slot >= Storage.Length) { - SEnvir.Log(string.Format("[BAD ITEM] Guild: {0}, Slot: {1}", GuildName, item.Slot)); + SEnvir.ServerLogger.Log(string.Format("[BAD ITEM] Guild: {0}, Slot: {1}", GuildName, item.Slot)); continue; } diff --git a/ServerLibrary/Envir/Commands/Command/Admin/ClearIPBlocks.cs b/ServerLibrary/Envir/Commands/Command/Admin/ClearIPBlocks.cs index db6b4209..7b536f0a 100644 --- a/ServerLibrary/Envir/Commands/Command/Admin/ClearIPBlocks.cs +++ b/ServerLibrary/Envir/Commands/Command/Admin/ClearIPBlocks.cs @@ -1,4 +1,5 @@ -using Server.Models; +using Server.Infrastructure.Network; +using Server.Models; namespace Server.Envir.Commands.Command.Admin { @@ -8,7 +9,7 @@ class ClearIPBlocks : AbstractCommand public override void Action(PlayerObject player) { - SEnvir.IPBlocks.Clear(); + SEnvir.IpManager.Reset(); //TODO: this should be injected but need to rewrite everything to use DI (AutoFac) because reflection based creation is not flexible enough } } } diff --git a/ServerLibrary/Envir/Commands/Command/Admin/GiveGameGold.cs b/ServerLibrary/Envir/Commands/Command/Admin/GiveGameGold.cs index bd1ba5de..53c50043 100644 --- a/ServerLibrary/Envir/Commands/Command/Admin/GiveGameGold.cs +++ b/ServerLibrary/Envir/Commands/Command/Admin/GiveGameGold.cs @@ -3,9 +3,11 @@ using Server.Envir.Commands.Command; using Server.Envir.Commands.Command.Admin; using Server.Envir.Commands.Exceptions; +using Server.Infrastructure.Service.Connection; using Server.Models; -namespace Server.Envir.Commands.Admin { +namespace Server.Envir.Commands.Admin +{ class GiveGameGold : AbstractParameterizedCommand { public override string VALUE => "GIVEGAMEGOLD"; public override int PARAMS_LENGTH => 3; diff --git a/ServerLibrary/Envir/Commands/Command/Admin/TakeCastle.cs b/ServerLibrary/Envir/Commands/Command/Admin/TakeCastle.cs index 6c036027..4a6b9fc6 100644 --- a/ServerLibrary/Envir/Commands/Command/Admin/TakeCastle.cs +++ b/ServerLibrary/Envir/Commands/Command/Admin/TakeCastle.cs @@ -2,6 +2,7 @@ using Library.SystemModels; using Server.DBModels; using Server.Envir.Commands.Exceptions; +using Server.Infrastructure.Network; using Server.Models; using System; using System.Linq; @@ -30,19 +31,7 @@ public override void Action(PlayerObject player, string[] vals) throw new UserCommandException(string.Format("No guild currently owns {0} castle.", castle.Name)); ownerGuild.Castle = null; - foreach (SConnection con in SEnvir.Connections) - { - switch (con.Stage) - { - case GameStage.Game: - case GameStage.Observer: - con.ReceiveChat(string.Format(con.Language.ConquestLost, ownerGuild.GuildName, castle.Name), MessageType.System); - break; - default: - continue; - } - } - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestLost, ownerGuild.GuildName, castle.Name)); SEnvir.Broadcast(new S.GuildCastleInfo { Index = castle.Index, Owner = string.Empty }); foreach (PlayerObject user in SEnvir.Players) @@ -53,19 +42,7 @@ public override void Action(PlayerObject player, string[] vals) else { player.Character.Account.GuildMember.Guild.Castle = castle; - foreach (SConnection con in SEnvir.Connections) - { - switch (con.Stage) - { - case GameStage.Game: - case GameStage.Observer: - con.ReceiveChat(string.Format(con.Language.ConquestCapture, player.Character.Account.GuildMember.Guild.GuildName, castle.Name), MessageType.System); - break; - default: - continue; - } - } - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestCapture, player.Character.Account.GuildMember.Guild.GuildName, castle.Name)); SEnvir.Broadcast(new S.GuildCastleInfo { Index = castle.Index, Owner = player.Character.Account.GuildMember.Guild.GuildName }); foreach (PlayerObject user in SEnvir.Players) user.ApplyCastleBuff(); diff --git a/ServerLibrary/Envir/Commands/ErrorHandlingCommandHandler.cs b/ServerLibrary/Envir/Commands/ErrorHandlingCommandHandler.cs index c8740d7c..1777b7cc 100644 --- a/ServerLibrary/Envir/Commands/ErrorHandlingCommandHandler.cs +++ b/ServerLibrary/Envir/Commands/ErrorHandlingCommandHandler.cs @@ -1,6 +1,7 @@ using Library; using Server.Envir.Commands.Exceptions; using Server.Envir.Commands.Handler; +using Server.Infrastructure.Service.Connection; using Server.Models; using System; using System.Collections.Generic; @@ -41,13 +42,13 @@ public void Handle(PlayerObject player, string[] commandParts) player.Connection.ReceiveChat(exception.Message, MessageType.System); if (!exception.userOnly) { - foreach (SConnection connection in player.Connection.Observers) + foreach (UserConnection connection in player.Connection.Observers) connection.ReceiveChat(exception.Message, MessageType.System); } } catch (Exception exception) { - SEnvir.Log("FatalCommandError [" + player.Name + "]: " + exception.Message); + SEnvir.ServerLogger.Log("FatalCommandError [" + player.Name + "]: " + exception.Message); player.Connection.ReceiveChat("FatalCommandError: The error has been logged. Contact an admin.", MessageType.System); } } diff --git a/ServerLibrary/Envir/Config.cs b/ServerLibrary/Envir/Config.cs index a0c9e84d..f15a3868 100644 --- a/ServerLibrary/Envir/Config.cs +++ b/ServerLibrary/Envir/Config.cs @@ -160,7 +160,7 @@ public static void LoadVersion() } catch (Exception ex) { - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); } } } diff --git a/ServerLibrary/Envir/SEnvir.cs b/ServerLibrary/Envir/SEnvir.cs index fd6a5a39..10690b34 100644 --- a/ServerLibrary/Envir/SEnvir.cs +++ b/ServerLibrary/Envir/SEnvir.cs @@ -7,6 +7,15 @@ using Server.Envir.Commands.Handler; using Server.Envir.Events; using Server.Envir.Events.Triggers; +using Server.Infrastructure.Logging; +using Server.Infrastructure.Logging.Formatter; +using Server.Infrastructure.Network.Http; +using Server.Infrastructure.Network.Smtp; +using Server.Infrastructure.Network.Tcp; +using Server.Infrastructure.Network.Tcp.ListenerHandler; +using Server.Infrastructure.Scheduler; +using Server.Infrastructure.Service; +using Server.Infrastructure.Service.Connection; using Server.Models; using System; using System.Collections.Concurrent; @@ -15,11 +24,8 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Net; -using System.Net.Sockets; using System.Reflection; using System.Security.Cryptography; -using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -31,194 +37,64 @@ namespace Server.Envir { public static class SEnvir { - #region Logging - - public static ConcurrentQueue DisplayLogs = []; - public static ConcurrentQueue Logs = []; - public static bool UseLogConsole = false; - - public static void Log(string log, bool hardLog = true) - { - log = string.Format("[{0:F}]: {1}", Time.Now, log); - - if (UseLogConsole) - { - Console.WriteLine(log); - } - else - { - if (DisplayLogs.Count < 100) - DisplayLogs.Enqueue(log); + //TODO: EVERYTHING static in here should be using DI ideally. - if (hardLog && Logs.Count < 1000) - Logs.Enqueue(log); - } - } - - public static ConcurrentQueue DisplayChatLogs = new ConcurrentQueue(); - public static ConcurrentQueue ChatLogs = new ConcurrentQueue(); - public static void LogChat(string log) - { - log = string.Format("[{0:F}]: {1}", Time.Now, log); - - if (DisplayChatLogs.Count < 500) - DisplayChatLogs.Enqueue(log); - - if (ChatLogs.Count < 1000) - ChatLogs.Enqueue(log); - } + public static readonly IpAddressService IpManager = new IpAddressService(); + public static readonly UserConnectionService UserConnectionService = new UserConnectionService( + new UserConnectionFactory(() => UserConnectionService.RemoveConnection), + IpManager + ); + public static readonly UserBroadcastService BroadcastService = new UserBroadcastService(UserConnectionService); + #region Logging + //TODO: these things should not exist here - need to move to DI then we can push this up outside of SEnvir + public static bool UseLogConsole = false; + public static readonly ConcurrentQueue ServerAppLogs = []; + public static readonly ConcurrentQueue ChatAppLogs = []; + //TODO-END + + private static SingleThreadScheduler LogWritingScheduler = new SingleThreadScheduler(); + private static readonly ILogFormatter LogFormatter = new StdLogFormatter(); + public static readonly ILogger ServerLogger = new CompositeLogger( + UseLogConsole ? [new SystemConsoleLogger(LogFormatter)] : [new SystemAppLogger(ServerAppLogs, LogFormatter), new SystemFileLogger(LogWritingScheduler, LogFormatter)] + ); + public static readonly ILogger UserChatLogger = new CompositeLogger( + UseLogConsole ? [new UserChatFileLogger(LogWritingScheduler,LogFormatter)] : [new UserChatAppLogger(ChatAppLogs, LogFormatter), new UserChatFileLogger(LogWritingScheduler, LogFormatter)] + ); #endregion - #region Network - - public static Dictionary IPBlocks = new Dictionary(); - public static Dictionary IPCount = new Dictionary(); - - public static List Connections = new List(); - public static ConcurrentQueue NewConnections; - - private static TcpListener _listener, _userCountListener; - - private static void StartNetwork(bool log = true) - { - try - { - NewConnections = new ConcurrentQueue(); - - _listener = new TcpListener(IPAddress.Parse(Config.IPAddress), Config.Port); - _listener.Start(); - _listener.BeginAcceptTcpClient(Connection, null); - - _userCountListener = new TcpListener(IPAddress.Parse(Config.IPAddress), Config.UserCountPort); - _userCountListener.Start(); - _userCountListener.BeginAcceptTcpClient(CountConnection, null); - - NetworkStarted = true; - if (log) Log($"Network Started. Listen: {Config.IPAddress}:{Config.Port}"); - } - catch (Exception ex) - { - Started = false; - Log(ex.ToString()); - } - } - private static void StopNetwork(bool log = true) - { - TcpListener expiredListener = _listener; - TcpListener expiredUserListener = _userCountListener; - - _listener = null; - _userCountListener = null; - - Started = false; - - expiredListener?.Stop(); - expiredUserListener?.Stop(); - - NewConnections = null; - - try - { - Packet p = new G.Disconnect { Reason = DisconnectReason.ServerClosing }; - for (int i = Connections.Count - 1; i >= 0; i--) - Connections[i].SendDisconnect(p); - - Thread.Sleep(2000); - } - catch (Exception ex) - { - Log(ex.ToString()); - } - - if (log) Log("Network Stopped."); - } - - private static void Connection(IAsyncResult result) - { - try - { - if (_listener == null || !_listener.Server.IsBound) return; - - TcpClient client = _listener.EndAcceptTcpClient(result); - - string ipAddress = client.Client.RemoteEndPoint.ToString().Split(':')[0]; - - if (!IPBlocks.TryGetValue(ipAddress, out DateTime banDate) || banDate < Now) - { - SConnection Connection = new SConnection(client); - - if (Connection.Connected) - NewConnections?.Enqueue(Connection); - } - } - catch (SocketException) - { - - } - catch (Exception ex) - { - Log(ex.ToString()); - } - finally - { - while (NewConnections?.Count >= 15) - Thread.Sleep(1); - - if (_listener != null && _listener.Server.IsBound) - _listener.BeginAcceptTcpClient(Connection, null); - } - } - - private static void CountConnection(IAsyncResult result) - { - try - { - if (_userCountListener == null || !_userCountListener.Server.IsBound) return; - - TcpClient client = _userCountListener.EndAcceptTcpClient(result); + #region TcpServer + private static readonly TcpServer UserTcpServer = new TcpServer( + new UserConnectionListenerHandler(UserConnectionService), + Config.IPAddress, + Config.Port + ); - byte[] data = Encoding.ASCII.GetBytes(string.Format("c;/Zircon/{0}/;", Connections.Count)); + private static readonly TcpServer ActiveUserCountTcpServer = new TcpServer( + new ActiveUserCountListenerHandler(() => UserConnectionService.ActiveConnections.Count), + Config.IPAddress, + Config.UserCountPort + ); + #endregion - client.Client.BeginSend(data, 0, data.Length, SocketFlags.None, CountConnectionEnd, client); - } - catch { } - finally - { - if (_userCountListener != null && _userCountListener.Server.IsBound) - _userCountListener.BeginAcceptTcpClient(CountConnection, null); - } - } - private static void CountConnectionEnd(IAsyncResult result) - { - try + public static bool Started //TODO: not sure what purpose of this was really.. it was 100% controlled by UserTcpServer + { + get => UserTcpServer.Started; + set { - TcpClient client = result.AsyncState as TcpClient; - - if (client == null) return; - - client.Client.EndSend(result); - - client.Client.Dispose(); - } - catch { } + if (UserTcpServer.Started == value) return; + if (value) UserTcpServer.Start(); + else UserTcpServer.Stop(); + } } - - #endregion - - public static bool Started { get; set; } - public static bool NetworkStarted { get; set; } public static bool Saving { get; private set; } + public static Thread EnvirThread { get; private set; } public static DateTime Now, StartTime, LastWarTime; public static int ProcessObjectCount, LoopCount; - public static long DBytesSent, DBytesReceived; - public static long TotalBytesSent, TotalBytesReceived; - public static long DownloadSpeed, UploadSpeed; - public static ICommandHandler CommandHandler = new ErrorHandlingCommandHandler( new PlayerCommandHandler(), new AdminCommandHandler() @@ -402,11 +278,11 @@ public static void LoadExperienceList() } catch (Exception) { - Log(string.Format("ExperienceList: Error parsing line {0} - {1}", i, lines[i])); + ServerLogger.Log(string.Format("ExperienceList: Error parsing line {0} - {1}", i, lines[i])); } } } - Log("Experience List Loaded."); + ServerLogger.Log("Experience List Loaded."); } private static void LoadDatabase() @@ -703,13 +579,13 @@ private static void CreateMovements(InstanceInfo instance = null, byte instanceS { if (movement.SourceRegion == null && movement.DestinationRegion == null) { - Log($"[Movement] No Source or Destination Region, Index: {movement.Index}"); + ServerLogger.Log($"[Movement] No Source or Destination Region, Index: {movement.Index}"); continue; } if (movement.SourceRegion == null) { - Log($"[Movement] No Source Region, Destination: {movement.DestinationRegion.ServerDescription}"); + ServerLogger.Log($"[Movement] No Source Region, Destination: {movement.DestinationRegion.ServerDescription}"); continue; } @@ -719,7 +595,7 @@ private static void CreateMovements(InstanceInfo instance = null, byte instanceS { if (instance == null) { - Log($"[Movement] Bad Source Map, Source: {movement.SourceRegion.ServerDescription}"); + ServerLogger.Log($"[Movement] Bad Source Map, Source: {movement.SourceRegion.ServerDescription}"); } continue; @@ -727,13 +603,13 @@ private static void CreateMovements(InstanceInfo instance = null, byte instanceS if (movement.DestinationRegion == null) { - Log($"[Movement] No Destination Region, Source: {movement.SourceRegion.ServerDescription}"); + ServerLogger.Log($"[Movement] No Destination Region, Source: {movement.SourceRegion.ServerDescription}"); continue; } if (movement.DestinationRegion.PointList.Count == 0) { - Log($"[Movement] Bad Destination, Dest: {movement.DestinationRegion.ServerDescription}, No Points"); + ServerLogger.Log($"[Movement] Bad Destination, Dest: {movement.DestinationRegion.ServerDescription}, No Points"); continue; } @@ -743,7 +619,7 @@ private static void CreateMovements(InstanceInfo instance = null, byte instanceS { if (instance == null) { - Log($"[Movement] Bad Destination Map, Destination: {movement.DestinationRegion.ServerDescription}"); + ServerLogger.Log($"[Movement] Bad Destination Map, Destination: {movement.DestinationRegion.ServerDescription}"); } if (movement.NeedInstance == null || movement.NeedInstance != instance) @@ -758,7 +634,7 @@ private static void CreateMovements(InstanceInfo instance = null, byte instanceS if (source == null) { - Log($"[Movement] Bad Origin, Source: {movement.SourceRegion.ServerDescription}, X:{sPoint.X}, Y:{sPoint.Y}"); + ServerLogger.Log($"[Movement] Bad Origin, Source: {movement.SourceRegion.ServerDescription}, X:{sPoint.X}, Y:{sPoint.Y}"); continue; } @@ -782,7 +658,7 @@ private static void CreateNPCs(InstanceInfo instance = null, byte instanceSequen { if (instance == null) { - Log(string.Format("[NPC] Bad Map, NPC: {0}, Map: {1}", info.NPCName, info.Region.ServerDescription)); + ServerLogger.Log(string.Format("[NPC] Bad Map, NPC: {0}, Map: {1}", info.NPCName, info.Region.ServerDescription)); } continue; @@ -794,7 +670,7 @@ private static void CreateNPCs(InstanceInfo instance = null, byte instanceSequen }; if (!ob.Spawn(info.Region, instance, instanceSequence)) - Log($"[NPC] Failed to spawn NPC, Region: {info.Region.ServerDescription}, NPC: {info.NPCName}"); + ServerLogger.Log($"[NPC] Failed to spawn NPC, Region: {info.Region.ServerDescription}, NPC: {info.NPCName}"); } } @@ -813,7 +689,7 @@ private static void CreateQuestRegions(InstanceInfo instance = null, byte instan { if (instance == null) { - Log($"[Quest Region] Bad Map, Map: {task.RegionParameter.ServerDescription}"); + ServerLogger.Log($"[Quest Region] Bad Map, Map: {task.RegionParameter.ServerDescription}"); } continue; @@ -825,7 +701,7 @@ private static void CreateQuestRegions(InstanceInfo instance = null, byte instan if (source == null) { - Log($"[Quest Region] Bad Quest Region, Source: {task.RegionParameter.ServerDescription}, X:{sPoint.X}, Y:{sPoint.Y}"); + ServerLogger.Log($"[Quest Region] Bad Quest Region, Source: {task.RegionParameter.ServerDescription}, X:{sPoint.X}, Y:{sPoint.Y}"); continue; } @@ -852,7 +728,7 @@ private static void CreateSafeZones(InstanceInfo instance = null, byte instanceS { if (instance == null) { - Log($"[Safe Zone] Bad Map, Map: {info.Region.ServerDescription}"); + ServerLogger.Log($"[Safe Zone] Bad Map, Map: {info.Region.ServerDescription}"); } continue; @@ -868,7 +744,7 @@ private static void CreateSafeZones(InstanceInfo instance = null, byte instanceS if (cell == null) { - Log($"[Safe Zone] Bad Location, Region: {info.Region.ServerDescription}, X: {point.X}, Y: {point.Y}."); + ServerLogger.Log($"[Safe Zone] Bad Location, Region: {info.Region.ServerDescription}, X: {point.X}, Y: {point.Y}."); continue; } @@ -910,7 +786,7 @@ private static void CreateSafeZones(InstanceInfo instance = null, byte instanceS if (map == null) { - Log($"[Safe Zone] Bad Bind Map, Map: {info.Region.ServerDescription}"); + ServerLogger.Log($"[Safe Zone] Bad Bind Map, Map: {info.Region.ServerDescription}"); continue; } @@ -921,7 +797,7 @@ private static void CreateSafeZones(InstanceInfo instance = null, byte instanceS if (cell == null) { - Log($"[Safe Zone] Bad Location, Region: {info.BindRegion.ServerDescription}, X: {point.X}, Y: {point.Y}."); + ServerLogger.Log($"[Safe Zone] Bad Location, Region: {info.BindRegion.ServerDescription}, X: {point.X}, Y: {point.Y}."); continue; } @@ -943,7 +819,7 @@ private static void CreateSpawns(InstanceInfo instance = null, byte instanceSequ { if (instance == null) { - Log(string.Format("[Respawn] Bad Map, Map: {0}", info.Region.ServerDescription)); + ServerLogger.Log(string.Format("[Respawn] Bad Map, Map: {0}", info.Region.ServerDescription)); } continue; @@ -1031,23 +907,19 @@ public static void EnvirLoop() DateTime DBTime = Now + Config.DBSaveDelay; StartEnvir(); - StartNetwork(); - - WebServer.StartWebServer(); - - Started = NetworkStarted; + UserTcpServer.Start(); + ServerLogger.Log($"Network Started. Listen: {Config.IPAddress}:{Config.Port}"); //TODO: maybe pass this into Start() method as a PostStartupHook param + ActiveUserCountTcpServer.Start(); + HttpWebServer.StartWebServer(); int count = 0, loopCount = 0; DateTime nextCount = Now.AddSeconds(1), UserCountTime = Now.AddMinutes(5), EventTimerTime = Now.AddMinutes(1), saveTime; - long previousTotalSent = 0, previousTotalReceived = 0; int lastindex = 0; long conDelay = 0; - Thread logThread = new Thread(WriteLogsLoop) { IsBackground = true }; - logThread.Start(); LastWarTime = Now; - Log($"Loading Time: {Functions.ToString(Time.Now - Now, true)}"); + ServerLogger.Log($"Loading Time: {Functions.ToString(Time.Now - Now, true)}"); while (Started) { @@ -1056,31 +928,7 @@ public static void EnvirLoop() try { - SConnection connection; - while (!NewConnections.IsEmpty) - { - if (!NewConnections.TryDequeue(out connection)) break; - - IPCount.TryGetValue(connection.IPAddress, out var ipCount); - - IPCount[connection.IPAddress] = ipCount + 1; - - Connections.Add(connection); - } - - long bytesSent = 0; - long bytesReceived = 0; - - for (int i = Connections.Count - 1; i >= 0; i--) - { - if (i >= Connections.Count) break; - - connection = Connections[i]; - - connection.Process(); - bytesSent += connection.TotalBytesSent; - bytesReceived += connection.TotalBytesReceived; - } + UserConnectionService.Process(); long delay = (Time.Now - Now).Ticks / TimeSpan.TicksPerMillisecond; if (delay > conDelay) @@ -1089,9 +937,6 @@ public static void EnvirLoop() for (int i = Players.Count - 1; i >= 0; i--) Players[i].StartProcess(); - TotalBytesSent = DBytesSent + bytesSent; - TotalBytesReceived = DBytesReceived + bytesReceived; - if (ServerBuffChanged) { for (int i = Players.Count - 1; i >= 0; i--) @@ -1126,8 +971,8 @@ public static void EnvirLoop() ActiveObjects.Remove(ob); ob.Activated = false; - Log(ex.Message); - Log(ex.StackTrace); + ServerLogger.Log(ex.Message); + ServerLogger.Log(ex.StackTrace); File.AppendAllText(@".\Errors.txt", ex.StackTrace + Environment.NewLine); } } @@ -1152,31 +997,10 @@ public static void EnvirLoop() loopCount = 0; conDelay = 0; - DownloadSpeed = TotalBytesReceived - previousTotalReceived; - UploadSpeed = TotalBytesSent - previousTotalSent; - - previousTotalReceived = TotalBytesReceived; - previousTotalSent = TotalBytesSent; - if (Now >= UserCountTime) { UserCountTime = Now.AddMinutes(5); - - foreach (SConnection conn in Connections) - { - conn.ReceiveChat(string.Format(conn.Language.OnlineCount, Players.Count, Connections.Count(x => x.Stage == GameStage.Observer)), MessageType.Hint); - - switch (conn.Stage) - { - case GameStage.Game: - if (conn.Player.Character.Observable) - conn.ReceiveChat(string.Format(conn.Language.ObserverCount, conn.Observers.Count), MessageType.Hint); - break; - case GameStage.Observer: - conn.ReceiveChat(string.Format(conn.Language.ObserverCount, conn.Observed.Observers.Count), MessageType.Hint); - break; - } - } + BroadcastService.BroadcastOnlineCount(); } if (Now >= EventTimerTime) @@ -1232,7 +1056,7 @@ public static void EnvirLoop() if (Config.EnableWebServer) { - WebServer.Process(); + HttpWebServer.Process(); } if (Config.ProcessGameGold) @@ -1272,21 +1096,21 @@ public static void EnvirLoop() { Session = null; - Log(ex.Message); - Log(ex.StackTrace); + ServerLogger.Log(ex.Message); + ServerLogger.Log(ex.StackTrace); File.AppendAllText(@".\Errors.txt", ex.StackTrace + Environment.NewLine); - Packet p = new G.Disconnect { Reason = DisconnectReason.Crashed }; - for (int i = Connections.Count - 1; i >= 0; i--) - Connections[i].SendDisconnect(p); + UserConnectionService.Broadcast(new G.Disconnect { Reason = DisconnectReason.Crashed }); Thread.Sleep(3000); break; } } - WebServer.StopWebServer(); - StopNetwork(); + HttpWebServer.StopWebServer(); + UserTcpServer.Stop(); + ActiveUserCountTcpServer.Stop(); + SEnvir.ServerLogger.Log("Network Stopped."); while (Saving) Thread.Sleep(1); if (Session != null) @@ -1299,17 +1123,17 @@ public static void EnvirLoop() public static void ProcessGameGold() { - while (!WebServer.Messages.IsEmpty) + while (!HttpWebServer.Messages.IsEmpty) { IPNMessage message; - if (!WebServer.Messages.TryDequeue(out message) || message == null) return; + if (!HttpWebServer.Messages.TryDequeue(out message) || message == null) return; - WebServer.PaymentList.Add(message); + HttpWebServer.PaymentList.Add(message); if (!message.Verified) { - SEnvir.Log("INVALID PAYPAL TRANSACTION " + message.Message); + SEnvir.ServerLogger.Log("INVALID PAYPAL TRANSACTION " + message.Message); continue; } @@ -1345,7 +1169,7 @@ public static void ProcessGameGold() if (SEnvir.GameGoldPaymentList[i].Status != paymentStatus) continue; - SEnvir.Log(string.Format("[Duplicated Transaction] ID:{0} Status:{1}.", transactionID, paymentStatus)); + SEnvir.ServerLogger.Log(string.Format("[Duplicated Transaction] ID:{0} Status:{1}.", transactionID, paymentStatus)); message.Duplicate = true; return; } @@ -1401,17 +1225,17 @@ public static void ProcessGameGold() case "Completed": break; } - if (payment.Status != WebServer.Completed) continue; + if (payment.Status != HttpWebServer.Completed) continue; //check that receiver_email is my primary paypal email if (string.Compare(payment.Receiver_EMail, Config.ReceiverEMail, StringComparison.OrdinalIgnoreCase) != 0) payment.Error = true; //check that paymentamount/current are correct - if (payment.Currency != WebServer.Currency) + if (payment.Currency != HttpWebServer.Currency) payment.Error = true; - if (WebServer.GoldTable.TryGetValue(payment.Price, out tempInt)) + if (HttpWebServer.GoldTable.TryGetValue(payment.Price, out tempInt)) payment.GameGoldAmount = tempInt; else payment.Error = true; @@ -1420,7 +1244,7 @@ public static void ProcessGameGold() if (character == null || payment.Error) { - SEnvir.Log($"[Transaction Error] ID:{transactionID} Status:{paymentStatus}, Amount{payment.Price}."); + SEnvir.ServerLogger.Log($"[Transaction Error] ID:{transactionID} Status:{paymentStatus}, Amount{payment.Price}."); continue; } @@ -1442,7 +1266,7 @@ public static void ProcessGameGold() } } - SEnvir.Log($"[Game Gold Purchase] Character: {character.CharacterName}, Amount: {payment.GameGoldAmount}."); + SEnvir.ServerLogger.Log($"[Game Gold Purchase] Character: {character.CharacterName}, Amount: {payment.GameGoldAmount}."); } } @@ -1453,7 +1277,7 @@ private static void Save() Saving = true; Session.Save(false); - WebServer.Save(); + HttpWebServer.Save(); Thread saveThread = new Thread(CommitChanges) { IsBackground = true }; saveThread.Start(Session); @@ -1463,54 +1287,10 @@ private static void CommitChanges(object data) Session session = (Session)data; session?.Commit(); - WebServer.CommitChanges(data); + HttpWebServer.CommitChanges(data); Saving = false; } - private static void WriteLogsLoop() - { - DateTime NextLogTime = Now.AddSeconds(10); - - while (Started) - { - if (Now < NextLogTime) - { - Thread.Sleep(1); - continue; - } - - WriteLogs(); - - NextLogTime = Now.AddSeconds(10); - } - } - private static void WriteLogs() - { - var logPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".\\Logs.txt")); - var chatLogPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".\\Chat Logs.txt")); - - List lines = new List(); - while (!Logs.IsEmpty) - { - if (!Logs.TryDequeue(out string line)) continue; - lines.Add(line); - } - - File.AppendAllLines(logPath, lines); - - lines.Clear(); - - while (!ChatLogs.IsEmpty) - { - if (!ChatLogs.TryDequeue(out string line)) continue; - lines.Add(line); - } - - File.AppendAllLines(chatLogPath, lines); - - lines.Clear(); - } - public static void CheckGuildWars() { TimeSpan change = Now - LastWarTime; @@ -2757,7 +2537,7 @@ public static void UpgradeLootBox(UserItem item) item.AddStat(Stat.Counter2, lootBoxInfo.Contents.Count <= 15 ? 2 : 1, StatSource.Added); // Step 1 = Randomise, 2 = Selection } - public static void Login(C.Login p, SConnection con) + public static void Login(C.Login p, UserConnection con) { AccountInfo account = null; bool admin = false; @@ -2765,7 +2545,7 @@ public static void Login(C.Login p, SConnection con) { account = GetCharacter(p.EMailAddress)?.Account; admin = true; - Log($"[Admin Attempted] Character: {p.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Admin Attempted] Character: {p.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } else { @@ -2823,7 +2603,7 @@ public static void Login(C.Login p, SConnection con) if (!admin && !PasswordMatch(p.Password, account.Password)) { - Log($"[Wrong Password] IP Address: {con.IPAddress}, Account: {account.EMailAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Wrong Password] IP Address: {con.IPAddress}, Account: {account.EMailAddress}, Security: {p.CheckSum}"); if (account.WrongPasswordCount++ >= 5) { @@ -2853,7 +2633,7 @@ public static void Login(C.Login p, SConnection con) // account.Connection.SendDisconnect(new G.Disconnect { Reason = DisconnectReason.AnotherUserAdmin }); } - Log($"[Account in Use] Account: {account.EMailAddress}, Current IP: {account.LastIP}, New IP: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Account in Use] Account: {account.EMailAddress}, Current IP: {account.LastIP}, New IP: {con.IPAddress}, Security: {p.CheckSum}"); if (account.TempAdmin) { @@ -2912,9 +2692,9 @@ public static void Login(C.Login p, SConnection con) account.LastSum = p.CheckSum; } - Log($"[Account Logon] Admin: {admin}, Account: {account.EMailAddress}, IP Address: {account.LastIP}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Account Logon] Admin: {admin}, Account: {account.EMailAddress}, IP Address: {account.LastIP}, Security: {p.CheckSum}"); } - public static void NewAccount(C.NewAccount p, SConnection con) + public static void NewAccount(C.NewAccount p, UserConnection con) { if (!Config.AllowNewAccount) { @@ -2965,13 +2745,8 @@ public static void NewAccount(C.NewAccount p, SConnection con) if (nowcount > 2 || todaycount > 5) { - IPBlocks[con.IPAddress] = Now.AddDays(7); - - for (int i = Connections.Count - 1; i >= 0; i--) - if (Connections[i].IPAddress == con.IPAddress) - Connections[i].Disconnecting = true; - - Log($"{con.IPAddress} Disconnected and banned for trying too many accounts"); + IpManager.Timeout(con, TimeSpan.FromDays(7)); + ServerLogger.Log($"{con.IPAddress} Disconnected and banned for trying too many accounts"); return; } @@ -3037,9 +2812,9 @@ public static void NewAccount(C.NewAccount p, SConnection con) con.Enqueue(new S.NewAccount { Result = NewAccountResult.Success }); - Log($"[Account Created] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Account Created] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void ChangePassword(C.ChangePassword p, SConnection con) + public static void ChangePassword(C.ChangePassword p, UserConnection con) { if (!Config.AllowChangePassword) { @@ -3100,7 +2875,7 @@ public static void ChangePassword(C.ChangePassword p, SConnection con) if (!PasswordMatch(p.CurrentPassword, account.Password)) { - Log($"[Wrong Password] IP Address: {con.IPAddress}, Account: {account.EMailAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Wrong Password] IP Address: {con.IPAddress}, Account: {account.EMailAddress}, Security: {p.CheckSum}"); if (account.WrongPasswordCount++ >= 5) { @@ -3120,9 +2895,9 @@ public static void ChangePassword(C.ChangePassword p, SConnection con) EmailService.SendChangePasswordEmail(account, con.IPAddress); con.Enqueue(new S.ChangePassword { Result = ChangePasswordResult.Success }); - Log($"[Password Changed] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Password Changed] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void RequestPasswordReset(C.RequestPasswordReset p, SConnection con) + public static void RequestPasswordReset(C.RequestPasswordReset p, UserConnection con) { if (!Config.AllowRequestPasswordReset) { @@ -3165,9 +2940,9 @@ public static void RequestPasswordReset(C.RequestPasswordReset p, SConnection co EmailService.SendResetPasswordRequestEmail(account, con.IPAddress); con.Enqueue(new S.RequestPasswordReset { Result = RequestPasswordResetResult.Success }); - Log($"[Request Password] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Request Password] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void ResetPassword(C.ResetPassword p, SConnection con) + public static void ResetPassword(C.ResetPassword p, UserConnection con) { if (!Config.AllowManualResetPassword) { @@ -3208,9 +2983,9 @@ public static void ResetPassword(C.ResetPassword p, SConnection con) EmailService.SendChangePasswordEmail(account, con.IPAddress); con.Enqueue(new S.ResetPassword { Result = ResetPasswordResult.Success }); - Log($"[Reset Password] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Reset Password] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void Activation(C.Activation p, SConnection con) + public static void Activation(C.Activation p, UserConnection con) { if (!Config.AllowManualActivation) { @@ -3237,9 +3012,9 @@ public static void Activation(C.Activation p, SConnection con) con.Enqueue(new S.Activation { Result = ActivationResult.Success }); - Log($"[Activation] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Activation] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void RequestActivationKey(C.RequestActivationKey p, SConnection con) + public static void RequestActivationKey(C.RequestActivationKey p, UserConnection con) { if (!Config.AllowRequestActivation) { @@ -3280,10 +3055,10 @@ public static void RequestActivationKey(C.RequestActivationKey p, SConnection co } EmailService.ResendActivationEmail(account); con.Enqueue(new S.RequestActivationKey { Result = RequestActivationKeyResult.Success }); - Log($"[Request Activation] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Request Activation] Account: {account.EMailAddress}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void NewCharacter(C.NewCharacter p, SConnection con) + public static void NewCharacter(C.NewCharacter p, UserConnection con) { if (!Config.AllowNewCharacter) { @@ -3438,9 +3213,9 @@ public static void NewCharacter(C.NewCharacter p, SConnection con) Character = cInfo.ToSelectInfo(), }); - Log($"[Character Created] Character: {p.CharacterName}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Character Created] Character: {p.CharacterName}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); } - public static void DeleteCharacter(C.DeleteCharacter p, SConnection con) + public static void DeleteCharacter(C.DeleteCharacter p, UserConnection con) { if (!Config.AllowDeleteCharacter) { @@ -3461,13 +3236,13 @@ public static void DeleteCharacter(C.DeleteCharacter p, SConnection con) character.Deleted = true; con.Enqueue(new S.DeleteCharacter { Result = DeleteCharacterResult.Success, DeletedIndex = character.Index }); - Log($"[Character Deleted] Character: {character.CharacterName}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); + ServerLogger.Log($"[Character Deleted] Character: {character.CharacterName}, IP Address: {con.IPAddress}, Security: {p.CheckSum}"); return; } con.Enqueue(new S.DeleteCharacter { Result = DeleteCharacterResult.NotFound }); } - public static void StartGame(C.StartGame p, SConnection con) + public static void StartGame(C.StartGame p, UserConnection con) { if (!Config.AllowStartGame) { @@ -3582,7 +3357,7 @@ public static PlayerObject GetPlayerByCharacter(string name) { return GetCharacter(name)?.Account.Connection?.Player; } - public static SConnection GetConnectionByCharacter(string name) + public static UserConnection GetConnectionByCharacter(string name) { return GetCharacter(name)?.Account.Connection; } @@ -3737,7 +3512,7 @@ public static Map GetMap(MapInfo info, InstanceInfo instance = null, byte instan CreateQuestRegions(instance, instanceSequence); - Log($"Loaded Instance {instance.Name} at index {instanceSequence}"); + ServerLogger.Log($"Loaded Instance {instance.Name} at index {instanceSequence}"); return instanceSequence; } @@ -3811,7 +3586,7 @@ public static void UnloadInstance(InstanceInfo instance, byte instanceSequence) } } - Log($"Unloaded Instance {instance.Name} at index {instanceSequence} and removed {users.Count} user records"); + ServerLogger.Log($"Unloaded Instance {instance.Name} at index {instanceSequence} and removed {users.Count} user records"); } public static UserConquestStats GetConquestStats(PlayerObject player) diff --git a/ServerLibrary/Infrastructure/Logging/CompositeLogger.cs b/ServerLibrary/Infrastructure/Logging/CompositeLogger.cs new file mode 100644 index 00000000..10578516 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/CompositeLogger.cs @@ -0,0 +1,12 @@ + +namespace Server.Infrastructure.Logging +{ + internal class CompositeLogger(params ILogger[] DelegateLoggers) : ILogger + { + public void Log(string message) + { + foreach (var logger in DelegateLoggers) + logger.Log(message); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/Formatter/ILogFormatter.cs b/ServerLibrary/Infrastructure/Logging/Formatter/ILogFormatter.cs new file mode 100644 index 00000000..42f53a15 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/Formatter/ILogFormatter.cs @@ -0,0 +1,8 @@ + +namespace Server.Infrastructure.Logging.Formatter +{ + public interface ILogFormatter + { + string Format(string message); + } +} diff --git a/ServerLibrary/Infrastructure/Logging/Formatter/StdLogFormatter.cs b/ServerLibrary/Infrastructure/Logging/Formatter/StdLogFormatter.cs new file mode 100644 index 00000000..3efd0199 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/Formatter/StdLogFormatter.cs @@ -0,0 +1,12 @@ +using Library; + +namespace Server.Infrastructure.Logging.Formatter +{ + public class StdLogFormatter : ILogFormatter + { + public string Format(string message) + { + return string.Format("[{0:F}]: {1}", Time.Now, message); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/ILogger.cs b/ServerLibrary/Infrastructure/Logging/ILogger.cs new file mode 100644 index 00000000..d95f38d3 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/ILogger.cs @@ -0,0 +1,8 @@ + +namespace Server.Infrastructure.Logging +{ + public interface ILogger + { + void Log(string message); + } +} diff --git a/ServerLibrary/Infrastructure/Logging/SystemAppLogger.cs b/ServerLibrary/Infrastructure/Logging/SystemAppLogger.cs new file mode 100644 index 00000000..2bb87ed7 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/SystemAppLogger.cs @@ -0,0 +1,15 @@ +using Server.Infrastructure.Logging.Formatter; +using System.Collections.Concurrent; + +namespace Server.Infrastructure.Logging +{ + //TODO: this should ideally be owned by Server and injected + internal class SystemAppLogger(ConcurrentQueue Logs, ILogFormatter LogFormatter) : ILogger + { + public void Log(string message) + { + if (Logs.Count < 100) + Logs.Enqueue(LogFormatter.Format(message)); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/SystemConsoleLogger.cs b/ServerLibrary/Infrastructure/Logging/SystemConsoleLogger.cs new file mode 100644 index 00000000..8e599db9 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/SystemConsoleLogger.cs @@ -0,0 +1,14 @@ +using Server.Infrastructure.Logging.Formatter; +using System; + +namespace Server.Infrastructure.Logging +{ + //TODO: this should ideally be owned by ServerCore and injected + public class SystemConsoleLogger(ILogFormatter LogFormatter) : ILogger + { + public void Log(string message) + { + Console.WriteLine(LogFormatter.Format(message)); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/SystemFileLogger.cs b/ServerLibrary/Infrastructure/Logging/SystemFileLogger.cs new file mode 100644 index 00000000..b4a6fb20 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/SystemFileLogger.cs @@ -0,0 +1,37 @@ +using Server.Infrastructure.Logging.Formatter; +using Server.Infrastructure.Scheduler; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; + +namespace Server.Infrastructure.Logging +{ + internal class SystemFileLogger : ILogger + { + private static readonly string LOG_PATH = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".\\Logs.txt")); + + private readonly ILogFormatter LogFormatter; + private readonly ConcurrentQueue Logs = []; + + public SystemFileLogger(SingleThreadScheduler scheduler, ILogFormatter logFormatter) + { + LogFormatter = logFormatter; + scheduler.ScheduleRecurring(WriteLogs, TimeSpan.FromSeconds(10)); + } + + public void Log(string message) + { + if (Logs.Count < 1000) //hardLog && - this is never used its always using default of true + Logs.Enqueue(LogFormatter.Format(message)); + } + + private void WriteLogs() + { + List lines = []; + while (Logs.TryDequeue(out string line)) + lines.Add(line); + File.AppendAllLines(LOG_PATH, lines); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/UserChatAppLogger.cs b/ServerLibrary/Infrastructure/Logging/UserChatAppLogger.cs new file mode 100644 index 00000000..8f0dcb0a --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/UserChatAppLogger.cs @@ -0,0 +1,15 @@ +using Server.Infrastructure.Logging.Formatter; +using System.Collections.Concurrent; + +namespace Server.Infrastructure.Logging +{ + //TODO: this should ideally be owned by Server and injected + internal class UserChatAppLogger(ConcurrentQueue Logs, ILogFormatter LogFormatter) : ILogger + { + public void Log(string message) + { + if (Logs.Count < 500) + Logs.Enqueue(LogFormatter.Format(message)); + } + } +} diff --git a/ServerLibrary/Infrastructure/Logging/UserChatFileLogger.cs b/ServerLibrary/Infrastructure/Logging/UserChatFileLogger.cs new file mode 100644 index 00000000..bbef5034 --- /dev/null +++ b/ServerLibrary/Infrastructure/Logging/UserChatFileLogger.cs @@ -0,0 +1,38 @@ +using Server.Envir; +using Server.Infrastructure.Logging.Formatter; +using Server.Infrastructure.Scheduler; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; + +namespace Server.Infrastructure.Logging +{ + internal class UserChatFileLogger : ILogger + { + private readonly string LOG_PATH = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".\\Chat Logs.txt")); + + private readonly ILogFormatter LogFormatter; + private readonly ConcurrentQueue Logs = []; + + public UserChatFileLogger(SingleThreadScheduler scheduler, ILogFormatter logFormatter) + { + LogFormatter = logFormatter; + scheduler.ScheduleRecurring(WriteLogs, TimeSpan.FromSeconds(10)); + } + + public void Log(string message) + { + if (Logs.Count < 1000) + Logs.Enqueue(LogFormatter.Format(message)); + } + + private void WriteLogs() + { + List lines = []; + while (Logs.TryDequeue(out string line)) + lines.Add(line); + File.AppendAllLines(LOG_PATH, lines); + } + } +} diff --git a/ServerLibrary/Envir/WebServer.cs b/ServerLibrary/Infrastructure/Network/Http/HttpWebServer.cs similarity index 93% rename from ServerLibrary/Envir/WebServer.cs rename to ServerLibrary/Infrastructure/Network/Http/HttpWebServer.cs index 349b250e..85259344 100644 --- a/ServerLibrary/Envir/WebServer.cs +++ b/ServerLibrary/Infrastructure/Network/Http/HttpWebServer.cs @@ -7,18 +7,18 @@ using System.Net; using System.Text; using System.Threading.Tasks; -using System.Web; -using G = Library.Network.GeneralPackets; -using S = Library.Network.ServerPackets; -using C = Library.Network.ClientPackets; using System.Net.Http; using MirDB; using System.IO.Compression; +using Server.Infrastructure.Network.Smtp; +using Server.Envir; -namespace Server.Envir +namespace Server.Infrastructure.Network.Http { - public static class WebServer + public static class HttpWebServer { + //TODO: separate the HttpWeb components from all the other stuff like GG processing + public static ConcurrentQueue WebCommandQueue; public static bool WebServerStarted { get; set; } @@ -49,7 +49,7 @@ public static class WebServer public static ConcurrentQueue Messages = new ConcurrentQueue(); public static List PaymentList = new List(), HandledPayments = new List(); - static WebServer() + static HttpWebServer() { Messages = new ConcurrentQueue(); @@ -90,16 +90,14 @@ public static void StartWebServer(bool log = true) IPNListener.Start(); IPNListener.BeginGetContext(IPNConnection, null); - - WebServerStarted = true; - if (log) SEnvir.Log("Web Server Started."); + if (log) SEnvir.ServerLogger.Log("Web Server Started."); } catch (Exception ex) { WebServerStarted = false; - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); if (WebListener != null && WebListener.IsListening) WebListener?.Stop(); @@ -132,7 +130,7 @@ public static void StopWebServer(bool log = true) expiredBuyListener?.Stop(); expiredIPNListener?.Stop(); - if (log) SEnvir.Log("Web Server Stopped."); + if (log) SEnvir.ServerLogger.Log("Web Server Stopped."); } private static void WebConnection(IAsyncResult result) @@ -173,21 +171,21 @@ private static void SystemDBSync(HttpListenerContext context) { if (!Config.AllowSystemDBSync) { - SEnvir.Log($"Trying sync but not enabled"); + SEnvir.ServerLogger.Log($"Trying sync but not enabled"); context.Response.StatusCode = 401; return; } if (context.Request.HttpMethod != "POST" || !context.Request.HasEntityBody) { - SEnvir.Log($"Trying sync but method is not post or not have body"); + SEnvir.ServerLogger.Log($"Trying sync but method is not post or not have body"); context.Response.StatusCode = 401; return; } if (context.Request.ContentLength64 > 1024 * 1024 * 10) { - SEnvir.Log($"Trying sync but exceeded SystemDB size"); + SEnvir.ServerLogger.Log($"Trying sync but exceeded SystemDB size"); context.Response.StatusCode = 400; return; } @@ -195,12 +193,12 @@ private static void SystemDBSync(HttpListenerContext context) var masterPassword = context.Request.QueryString["Key"]; if (string.IsNullOrEmpty(masterPassword) || !masterPassword.Equals(Config.SyncKey)) { - SEnvir.Log($"Trying sync but key received is not valid"); + SEnvir.ServerLogger.Log($"Trying sync but key received is not valid"); context.Response.StatusCode = 400; return; } - SEnvir.Log($"Starting remote syncronization..."); + SEnvir.ServerLogger.Log($"Starting remote syncronization..."); var buffer = new byte[context.Request.ContentLength64]; var offset = 0; @@ -230,7 +228,7 @@ private static void SystemDBSync(HttpListenerContext context) context.Response.StatusCode = 200; - SEnvir.Log($"Syncronization completed..."); + SEnvir.ServerLogger.Log($"Syncronization completed..."); } catch (Exception ex) { @@ -238,7 +236,7 @@ private static void SystemDBSync(HttpListenerContext context) context.Response.ContentType = "text/plain"; var message = Encoding.UTF8.GetBytes(ex.ToString()); context.Response.OutputStream.Write(message, 0, message.Length); - SEnvir.Log("Syncronization exception: " + ex.ToString()); + SEnvir.ServerLogger.Log("Syncronization exception: " + ex.ToString()); } finally { @@ -405,7 +403,7 @@ private static void IPNConnection(IAsyncResult result) } catch (Exception ex) { - SEnvir.Log(ex.ToString()); + SEnvir.ServerLogger.Log(ex.ToString()); } finally { diff --git a/ServerLibrary/Envir/EmailService.cs b/ServerLibrary/Infrastructure/Network/Smtp/EmailService.cs similarity index 89% rename from ServerLibrary/Envir/EmailService.cs rename to ServerLibrary/Infrastructure/Network/Smtp/EmailService.cs index 5804f6d4..b4c5fa3a 100644 --- a/ServerLibrary/Envir/EmailService.cs +++ b/ServerLibrary/Infrastructure/Network/Smtp/EmailService.cs @@ -1,16 +1,18 @@ using Library; using Server.DBModels; +using Server.Envir; +using Server.Infrastructure.Network.Http; using System; -using System.Collections.Generic; using System.Net; using System.Net.Mail; -using System.Text; using System.Threading.Tasks; -namespace Server.Envir +namespace Server.Infrastructure.Network.Smtp { public static class EmailService { + //TODO: separate SMTP components and game related components (i.e AccountInfo etc). + public static int EMailsSent; public static void SendActivationEmail(AccountInfo account) @@ -40,12 +42,12 @@ public static void SendActivationEmail(AccountInfo account) Body = $"Dear {account.RealName},

" + $"Thank you for registering a Zircon account, before you can log in to the game, you are required to activate your account.

" + $"To complete your registration and activate the account please visit the following link:
" + - $"Click here to Activate

" + + $"Click here to Activate

" + $"If the above link does not work please use the following Activation Key when you next attempt to log in to your account
" + $"Activation Key: {account.ActivationKey}

" + (account.Referral != null ? $"You were referred by: {account.Referral.EMailAddress}

" : "") + $"If you did not create this account and want to cancel the registration to delete this account please visit the following link:
" + - $"Click here to Delete Account

" + + $"Click here to Delete Account

" + $"We'll see you in game
" + $"Zircon Server" }; @@ -57,8 +59,8 @@ public static void SendActivationEmail(AccountInfo account) } catch (Exception ex) { - SEnvir.Log(ex.Message); - SEnvir.Log(ex.StackTrace); + SEnvir.ServerLogger.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.StackTrace); } }); } @@ -105,8 +107,8 @@ public static void ResendActivationEmail(AccountInfo account) } catch (Exception ex) { - SEnvir.Log(ex.Message); - SEnvir.Log(ex.StackTrace); + SEnvir.ServerLogger.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.StackTrace); } }); } @@ -149,8 +151,8 @@ public static void SendChangePasswordEmail(AccountInfo account, string ipAddress } catch (Exception ex) { - SEnvir.Log(ex.Message); - SEnvir.Log(ex.StackTrace); + SEnvir.ServerLogger.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.StackTrace); } }); } @@ -180,7 +182,7 @@ public static void SendResetPasswordRequestEmail(AccountInfo account, string ipA $"A request to reset your password has been made.
" + $"IP Address: {ipAddress}

" + $"To reset your password please click on the following link:
" + - $"Reset Password

" + + $"Reset Password

" + $"If the above link does not work please use the following Reset Key to reset your password
" + $"Reset Key: {account.ResetKey}

" + $"If you did not request this reset, please ignore this email as your password will not be changed.

" + @@ -195,8 +197,8 @@ public static void SendResetPasswordRequestEmail(AccountInfo account, string ipA } catch (Exception ex) { - SEnvir.Log(ex.Message); - SEnvir.Log(ex.StackTrace); + SEnvir.ServerLogger.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.StackTrace); } }); } @@ -237,8 +239,8 @@ public static void SendResetPasswordEmail(AccountInfo account, string password) } catch (Exception ex) { - SEnvir.Log(ex.Message); - SEnvir.Log(ex.StackTrace); + SEnvir.ServerLogger.Log(ex.Message); + SEnvir.ServerLogger.Log(ex.StackTrace); } }); } diff --git a/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/ActiveUserCountListenerHandler.cs b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/ActiveUserCountListenerHandler.cs new file mode 100644 index 00000000..ec995b68 --- /dev/null +++ b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/ActiveUserCountListenerHandler.cs @@ -0,0 +1,44 @@ +using System; +using System.Net.Sockets; +using System.Text; + +namespace Server.Infrastructure.Network.Tcp.ListenerHandler +{ + public class ActiveUserCountListenerHandler(Func UserCountSupplier) : IListenerHandler + { + public void OnAcceptBegin(TcpListener l, IAsyncResult r) + { + TcpClient client = l.EndAcceptTcpClient(r); + byte[] data = Encoding.ASCII.GetBytes(string.Format("c;/Zircon/{0}/;", UserCountSupplier.Invoke())); + client.Client.BeginSend(data, 0, data.Length, SocketFlags.None, TerminateConnection, client); + } + + public void OnAcceptEnd(TcpListener l, IAsyncResult r) + { + // Do Nothing + } + + public void OnAcceptException(Exception ex) + { + // Do Nothing + } + + public void OnTermination() + { + // Do Nothing + } + + private void TerminateConnection(IAsyncResult result) + { + try + { + var client = result.AsyncState as TcpClient; + + if (client == null) return; + client.Client.EndSend(result); + client.Client.Dispose(); + } + catch { } + } + } +} diff --git a/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/IListenerHandler.cs b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/IListenerHandler.cs new file mode 100644 index 00000000..2a11dd55 --- /dev/null +++ b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/IListenerHandler.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Infrastructure.Network.Tcp.ListenerHandler +{ + public interface IListenerHandler + { + public void OnAcceptBegin(TcpListener listener, IAsyncResult result); + public void OnAcceptEnd(TcpListener listener, IAsyncResult result); + public void OnAcceptException(Exception ex); + + public void OnTermination(); + } +} diff --git a/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/UserConnectionListenerHandler.cs b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/UserConnectionListenerHandler.cs new file mode 100644 index 00000000..2df3a691 --- /dev/null +++ b/ServerLibrary/Infrastructure/Network/Tcp/ListenerHandler/UserConnectionListenerHandler.cs @@ -0,0 +1,33 @@ +using Server.Envir; +using Server.Infrastructure.Service.Connection; +using System; +using System.Net.Sockets; +using System.Threading; + +namespace Server.Infrastructure.Network.Tcp.ListenerHandler +{ + public class UserConnectionListenerHandler(AbstractConnectionService ConnectionService) : IListenerHandler + { + public void OnAcceptBegin(TcpListener listener, IAsyncResult result) + { + ConnectionService.Add(listener.EndAcceptTcpClient(result)); + } + + public void OnAcceptEnd(TcpListener listener, IAsyncResult result) + { + + while (ConnectionService.AcceptingConnections) + Thread.Sleep(1); + } + + public void OnAcceptException(Exception ex) + { + SEnvir.ServerLogger.Log(ex.ToString()); + } + + public void OnTermination() + { + ConnectionService.Reset(); + } + } +} diff --git a/ServerLibrary/Infrastructure/Network/Tcp/TcpServer.cs b/ServerLibrary/Infrastructure/Network/Tcp/TcpServer.cs new file mode 100644 index 00000000..88d2cf29 --- /dev/null +++ b/ServerLibrary/Infrastructure/Network/Tcp/TcpServer.cs @@ -0,0 +1,61 @@ +using Server.Envir; +using Server.Infrastructure.Network.Tcp.ListenerHandler; +using System; +using System.Net; +using System.Net.Sockets; + +namespace Server.Infrastructure.Network.Tcp +{ + public class TcpServer(IListenerHandler ListenerHandler, string IpAddress, ushort Port) + { + public bool Started { get; private set; } + + private TcpListener Listener; + + public void Start() + { + try + { + Listener = new TcpListener(IPAddress.Parse(IpAddress), Port); + Listener.Start(); + Listener.BeginAcceptTcpClient(HandleConnection, null); + Started = true; + } + catch (Exception ex) + { + Started = false; + SEnvir.ServerLogger.Log(ex.ToString()); + } + } + + public void Stop() + { + TcpListener expiredListener = Listener; + + Listener = null; + Started = false; + expiredListener?.Stop(); + ListenerHandler.OnTermination(); + } + + private void HandleConnection(IAsyncResult result) + { + try + { + if (Listener == null || !Listener.Server.IsBound) return; + ListenerHandler.OnAcceptBegin(Listener, result); + } + catch (SocketException) { } + catch (Exception ex) + { + ListenerHandler.OnAcceptException(ex); + } + finally + { + ListenerHandler.OnAcceptEnd(Listener, result); + if (Listener != null && Listener.Server.IsBound) + Listener.BeginAcceptTcpClient(HandleConnection, null); + } + } + } +} diff --git a/ServerLibrary/Infrastructure/Scheduler/RecurringAction.cs b/ServerLibrary/Infrastructure/Scheduler/RecurringAction.cs new file mode 100644 index 00000000..7c9661a6 --- /dev/null +++ b/ServerLibrary/Infrastructure/Scheduler/RecurringAction.cs @@ -0,0 +1,6 @@ +using System; + +namespace Server.Infrastructure.Scheduler +{ + internal record RecurringAction(Action Action, TimeSpan Interval) { } +} diff --git a/ServerLibrary/Infrastructure/Scheduler/SingleThreadScheduler.cs b/ServerLibrary/Infrastructure/Scheduler/SingleThreadScheduler.cs new file mode 100644 index 00000000..5778fb3f --- /dev/null +++ b/ServerLibrary/Infrastructure/Scheduler/SingleThreadScheduler.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Server.Infrastructure.Scheduler +{ + public class SingleThreadScheduler + { + private readonly Thread Thread; + + private readonly object Lock = new(); + private readonly PriorityQueue ScheduledActions = new(); + + public SingleThreadScheduler(string schedulerName = "default") + { + Thread = new Thread(Loop) + { + IsBackground = true, + Name = $"SingleThreadScheduler[{schedulerName}-{Guid.NewGuid()}]" + }; + Thread.Start(); + } + + public void ScheduleRecurring(Action action, TimeSpan interval) + { + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(interval, TimeSpan.Zero); + lock (Lock) + { + ScheduledActions.Enqueue(new RecurringAction(action, interval), DateTime.UtcNow); + Monitor.Pulse(Lock); + } + } + + private void Loop() + { + while (true) //TODO: should i bother to add state (Running, Start, Stop)? + { + foreach (var action in GetAvailableActions()) + { + try { action(); } + catch (Exception) { } + } + lock (Lock) Monitor.Wait(Lock, NextScheduledTime()); + } + } + + private List GetAvailableActions() + { + List actionsDue = []; + var now = DateTime.UtcNow; + + lock (Lock) + { + while (ScheduledActions.TryPeek(out var action, out var nextTime) && nextTime <= now) + { + var awd = ScheduledActions.Dequeue(); + actionsDue.Add(awd.Action); + ScheduledActions.Enqueue(awd, now.Add(awd.Interval)); + } + return actionsDue; + } + } + + private TimeSpan NextScheduledTime() + { + if (ScheduledActions.TryPeek(out var _, out var nextTime)) + { + var nextSchedule = nextTime - DateTime.UtcNow; + if (nextSchedule < TimeSpan.Zero) return TimeSpan.Zero; + return nextSchedule; + } + return TimeSpan.FromMilliseconds(int.MaxValue); + } + } +} diff --git a/ServerLibrary/Infrastructure/Service/Connection/AbstractConnectionService.cs b/ServerLibrary/Infrastructure/Service/Connection/AbstractConnectionService.cs new file mode 100644 index 00000000..ceb7cd99 --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/Connection/AbstractConnectionService.cs @@ -0,0 +1,133 @@ +using Library; +using Library.Network; +using Server.Envir; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Threading; + +namespace Server.Infrastructure.Service.Connection +{ + public abstract class AbstractConnectionService(IConnectionFactory ConnectionFactory, IpAddressService IpManager) where ConnectionType : BaseConnection + { + //public static Dictionary IPCount = new Dictionary(); //TODO: this isn't used but i imagine its intended to limit connections from single IP + + private bool IsResetting = false; + + public ConcurrentQueue PendingConnections = []; + public List ActiveConnections = []; + + public bool AcceptingConnections => PendingConnections?.Count >= 15 || IsResetting; + + #region Metadata + public long DBytesSent, DBytesReceived; //TODO: not sure why these DBytesX exist? They get assigned to TotalBytesX but only get incremented when someone disconnects (incremented by total of TotalBytesX) + + public long TotalBytesSent, TotalBytesReceived; + public long PreviousTotalReceived, PreviousTotalSent; + + public long DownloadSpeed => TotalBytesReceived - PreviousTotalReceived; + public long UploadSpeed => TotalBytesSent - PreviousTotalSent; + #endregion + + internal void Add(TcpClient tcpClient) + { + string ipAddress = tcpClient.Client.RemoteEndPoint.ToString().Split(':')[0]; //TODO: this is duplicated inside BaseConnection - create a utility method + if (IpManager.IsAllowing(ipAddress)) + { + var connection = ConnectionFactory.Create(tcpClient); + if (connection.Connected) PendingConnections?.Enqueue(connection); + } + } + + internal void Process() + { + while (!PendingConnections.IsEmpty) + { + if (!PendingConnections.TryDequeue(out var connection)) break; + + //IPCount.TryGetValue(connection.IPAddress, out var ipCount); + //IPCount[connection.IPAddress] = ipCount + 1; + ActiveConnections.Add(connection); + } + + long bytesSent = 0; + long bytesReceived = 0; + for (int i = ActiveConnections.Count - 1; i >= 0; i--) + { + if (i >= ActiveConnections.Count) break; + + var connection = ActiveConnections[i]; + connection.Process(); + + if (connection.TotalPacketsProcessed == 0 && connection.TotalBytesReceived > 1024) + { + connection.TryDisconnect(); + IpManager.Timeout(connection, Config.PacketBanTime); + SEnvir.ServerLogger.Log($"{connection.IPAddress} Disconnected, Large Packet"); + return; + } + + if (connection.ReceiveList?.Count > Config.MaxPacket) + { + connection.TryDisconnect(); + IpManager.Timeout(connection, Config.PacketBanTime); + SEnvir.ServerLogger.Log($"{connection.IPAddress} Disconnected, Large amount of Packets"); + return; + } + + bytesSent += connection.TotalBytesSent; + bytesReceived += connection.TotalBytesReceived; + } + + TotalBytesSent = DBytesSent + bytesSent; + TotalBytesReceived = DBytesReceived + bytesReceived; + PreviousTotalReceived = TotalBytesReceived; + PreviousTotalSent = TotalBytesSent; + } + + internal void Broadcast(Packet packet) + { + for (int i = ActiveConnections.Count - 1; i >= 0; i--) + ActiveConnections[i].SendDisconnect(packet); + } + + internal void Broadcast(Func packet) + { + for (int i = ActiveConnections.Count - 1; i >= 0; i--) + ActiveConnections[i].SendDisconnect(packet.Invoke(ActiveConnections[i])); + } + + internal void RemoveConnection(ConnectionType connection) + { + if (!ActiveConnections.Contains(connection)) + throw new InvalidOperationException("Connection was not found in list"); //TODO: why do we want to cause an exception here? Why not just log a warning and return? + + ActiveConnections.Remove(connection); + //IPCount[connection.IPAddress]--; + DBytesSent += TotalBytesSent; + DBytesReceived += TotalBytesReceived; + } + + internal void Reset() + { + IsResetting = true; + PendingConnections?.Clear(); + + try + { + Packet p = new Library.Network.GeneralPackets.Disconnect { Reason = DisconnectReason.ServerClosing }; + for (int i = ActiveConnections.Count - 1; i >= 0; i--) + ActiveConnections[i].SendDisconnect(p); + + Thread.Sleep(2000); // wait for disconnects + } + catch (Exception ex) + { + SEnvir.ServerLogger.Log(ex.ToString()); + } + + IsResetting = false; + } + } +} diff --git a/ServerLibrary/Infrastructure/Service/Connection/IConnectionFactory.cs b/ServerLibrary/Infrastructure/Service/Connection/IConnectionFactory.cs new file mode 100644 index 00000000..f9849003 --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/Connection/IConnectionFactory.cs @@ -0,0 +1,10 @@ +using Library.Network; +using System.Net.Sockets; + +namespace Server.Infrastructure.Service.Connection +{ + public interface IConnectionFactory where ConnectionType : BaseConnection + { + public ConnectionType Create(TcpClient tcpClient); + } +} diff --git a/ServerLibrary/Envir/SConnection.cs b/ServerLibrary/Infrastructure/Service/Connection/UserConnection.cs similarity index 94% rename from ServerLibrary/Envir/SConnection.cs rename to ServerLibrary/Infrastructure/Service/Connection/UserConnection.cs index 0fbb9c08..71b7c835 100644 --- a/ServerLibrary/Envir/SConnection.cs +++ b/ServerLibrary/Infrastructure/Service/Connection/UserConnection.cs @@ -2,6 +2,7 @@ using Library.Network; using Library.SystemModels; using Server.DBModels; +using Server.Envir; using Server.Envir.Translations; using Server.Models; using System; @@ -13,9 +14,9 @@ using G = Library.Network.GeneralPackets; using S = Library.Network.ServerPackets; -namespace Server.Envir +namespace Server.Infrastructure.Service.Connection { - public sealed class SConnection : BaseConnection + public sealed class UserConnection : BaseConnection { private static int SessionCount; @@ -28,33 +29,35 @@ public sealed class SConnection : BaseConnection public GameStage Stage { get; set; } public AccountInfo Account { get; set; } public PlayerObject Player { get; set; } - public string IPAddress { get; } public int SessionID { get; } - public SConnection Observed; - public List Observers = new List(); + public UserConnection Observed; + public List Observers = new List(); public List MPSearchResults = new List(); public HashSet VisibleResults = new HashSet(); public StringMessages Language; - public SConnection(TcpClient client) : base(client) + public Action DisconnectCallback; + + public UserConnection(TcpClient client, Action disconnectCallback) : base(client) { - IPAddress = client.Client.RemoteEndPoint.ToString().Split(':')[0]; + DisconnectCallback = disconnectCallback; + SessionID = ++SessionCount; Language = (StringMessages)ConfigReader.ConfigObjects[typeof(EnglishMessages)]; OnException += (o, e) => { - SEnvir.Log(string.Format("Crashed: Account: {0}, Character: {1}.", Account?.EMailAddress, Player?.Name)); - SEnvir.Log(e.ToString()); - SEnvir.Log(e.StackTrace.ToString()); + SEnvir.ServerLogger.Log(string.Format("Crashed: Account: {0}, Character: {1}.", Account?.EMailAddress, Player?.Name)); + SEnvir.ServerLogger.Log(e.ToString()); + SEnvir.ServerLogger.Log(e.StackTrace.ToString()); File.AppendAllText(@".\Errors.txt", e.StackTrace + Environment.NewLine); }; - SEnvir.Log(string.Format("[Connection] IP Address:{0}", IPAddress)); + SEnvir.ServerLogger.Log(string.Format("[Connection] IP Address:{0}", IPAddress)); UpdateTimeOut(); BeginReceive(); @@ -62,29 +65,21 @@ public SConnection(TcpClient client) : base(client) Enqueue(new G.Connected()); } + //TODO: whats the difference between Disconnect and SendDisconnect? Can we consolodate the two? public override void Disconnect() { if (!Connected) return; - base.Disconnect(); - CleanUp(); - - if (!SEnvir.Connections.Contains(this)) - throw new InvalidOperationException("Connection was not found in list"); - - SEnvir.Connections.Remove(this); - SEnvir.IPCount[IPAddress]--; - SEnvir.DBytesSent += TotalBytesSent; - SEnvir.DBytesReceived += TotalBytesReceived; + DisconnectCallback.Invoke(this); } public override void SendDisconnect(Packet p) { base.SendDisconnect(p); - CleanUp(); } + public override void TryDisconnect() { if (Stage == GameStage.Game) @@ -164,7 +159,7 @@ public void CleanUp() Observed = null; // ItemList.Clear(); - // MagicList.Clear(); + // MagicList.Clear(); } public override void Process() @@ -175,33 +170,6 @@ public override void Process() PingSent = true; Enqueue(new G.Ping { ObserverPacket = false }); } - - if (TotalPacketsProcessed == 0 && TotalBytesReceived > 1024) - { - TryDisconnect(); - SEnvir.IPBlocks[IPAddress] = SEnvir.Now.Add(Config.PacketBanTime); - - for (int i = SEnvir.Connections.Count - 1; i >= 0; i--) - if (SEnvir.Connections[i].IPAddress == IPAddress) - SEnvir.Connections[i].TryDisconnect(); - - SEnvir.Log($"{IPAddress} Disconnected, Large Packet"); - return; - } - - if (ReceiveList.Count > Config.MaxPacket) - { - TryDisconnect(); - SEnvir.IPBlocks[IPAddress] = SEnvir.Now.Add(Config.PacketBanTime); - - for (int i = SEnvir.Connections.Count - 1; i >= 0; i--) - if (SEnvir.Connections[i].IPAddress == IPAddress) - SEnvir.Connections[i].TryDisconnect(); - - SEnvir.Log($"{IPAddress} Disconnected, Large amount of Packets"); - return; - } - base.Process(); } @@ -211,7 +179,7 @@ public override void Enqueue(Packet p) if (p == null || !p.ObserverPacket) return; - foreach (SConnection observer in Observers) + foreach (UserConnection observer in Observers) observer.Enqueue(p); } @@ -236,11 +204,11 @@ public void ReceiveChat(string text, MessageType type, List link } } - public void ReceiveChatWithObservers(Func messageFunc, MessageType messageType, List linkedItems = null, uint objectID = 0) + public void ReceiveChatWithObservers(Func messageFunc, MessageType messageType, List linkedItems = null, uint objectID = 0) { ReceiveChat(messageFunc(this), messageType, linkedItems, objectID); - foreach (SConnection observer in Observers) + foreach (UserConnection observer in Observers) observer.ReceiveChat(messageFunc(observer), messageType, linkedItems, objectID); } @@ -626,7 +594,7 @@ public void Process(C.NPCClose p) Player.NPC = null; Player.NPCPage = null; - foreach (SConnection con in Observers) + foreach (UserConnection con in Observers) { con.Enqueue(new S.NPCClose()); } @@ -758,13 +726,13 @@ public void Process(C.RankSearch p) public void Process(C.ObserverRequest p) { - if (!Config.AllowObservation && (Account == null || (!Account.TempAdmin && !Account.Observer))) return; + if (!Config.AllowObservation && (Account == null || !Account.TempAdmin && !Account.Observer)) return; PlayerObject player = SEnvir.GetPlayerByCharacter(p.Name); if (player == null || player == Player) return; - if (!player.Character.Observable && (Account == null || (!Account.TempAdmin && !Account.Observer))) return; + if (!player.Character.Observable && (Account == null || !Account.TempAdmin && !Account.Observer)) return; if (Stage == GameStage.Game) Player.StopGame(); diff --git a/ServerLibrary/Infrastructure/Service/Connection/UserConnectionFactory.cs b/ServerLibrary/Infrastructure/Service/Connection/UserConnectionFactory.cs new file mode 100644 index 00000000..55178cc6 --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/Connection/UserConnectionFactory.cs @@ -0,0 +1,15 @@ +using System; +using System.Net.Sockets; + +namespace Server.Infrastructure.Service.Connection +{ + internal class UserConnectionFactory(Func> disconnectCallback) : IConnectionFactory + { + private readonly Func> DisconnectCallback = disconnectCallback; + + public UserConnection Create(TcpClient tcpClient) + { + return new UserConnection(tcpClient, DisconnectCallback.Invoke()); + } + } +} diff --git a/ServerLibrary/Infrastructure/Service/Connection/UserConnectionService.cs b/ServerLibrary/Infrastructure/Service/Connection/UserConnectionService.cs new file mode 100644 index 00000000..214b5707 --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/Connection/UserConnectionService.cs @@ -0,0 +1,12 @@ +using System; +using System.Linq; + +namespace Server.Infrastructure.Service.Connection +{ + public class UserConnectionService(IConnectionFactory connectionFactory, IpAddressService IpManager) + : AbstractConnectionService(connectionFactory, IpManager) + { + public int Players => ActiveConnections?.Count(c => c.Stage == GameStage.Game) ?? 0; + public int Observers => ActiveConnections?.Count(c => c.Stage == GameStage.Observer) ?? 0; + } +} diff --git a/ServerLibrary/Infrastructure/Service/IpAddressService.cs b/ServerLibrary/Infrastructure/Service/IpAddressService.cs new file mode 100644 index 00000000..c12c82ae --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/IpAddressService.cs @@ -0,0 +1,30 @@ +using Library.Network; +using Server.Envir; +using System; +using System.Collections.Generic; + +namespace Server.Infrastructure.Service +{ + public class IpAddressService + { + public Dictionary IpAddressTimeOuts = []; + + public bool IsAllowing(string ipAddress) + { + return !IpAddressTimeOuts.TryGetValue(ipAddress, out DateTime timeout) || timeout <= SEnvir.Now; + } + + public void Timeout(BaseConnection connection, TimeSpan duration) + { + var bannedUntil = DateTime.Now.Add(duration); + if (!IpAddressTimeOuts.TryAdd(connection.IPAddress, bannedUntil)) + IpAddressTimeOuts[connection.IPAddress] = bannedUntil; + connection.TryDisconnect(); + } + + public void Reset() + { + IpAddressTimeOuts.Clear(); + } + } +} diff --git a/ServerLibrary/Infrastructure/Service/UserBroadcastService.cs b/ServerLibrary/Infrastructure/Service/UserBroadcastService.cs new file mode 100644 index 00000000..26c1e89b --- /dev/null +++ b/ServerLibrary/Infrastructure/Service/UserBroadcastService.cs @@ -0,0 +1,64 @@ +using Library; +using Server.Infrastructure.Service.Connection; +using System; +using System.Collections.Generic; + +namespace Server.Infrastructure.Service +{ + public class UserBroadcastService(UserConnectionService UserConnectionService) + { + //TODO: some of this is convoluted because Language is owned by Server. It should be owned by client server should just send (i.e S.TemplatedMessage { type = 1, args = [...] } and let the client handle local translations + //TODO: not 100% happy - any other improvements? + + internal void BroadcastOnlineCount() + { + foreach (UserConnection conn in UserConnectionService.ActiveConnections) + { + conn.ReceiveChat(string.Format(conn.Language.OnlineCount, UserConnectionService.Players, UserConnectionService.Observers), MessageType.Hint); + + switch (conn.Stage) + { + case GameStage.Game: + if (conn.Player.Character.Observable) + conn.ReceiveChat(string.Format(conn.Language.ObserverCount, conn.Observers.Count), MessageType.Hint); + break; + case GameStage.Observer: + conn.ReceiveChat(string.Format(conn.Language.ObserverCount, conn.Observed.Observers.Count), MessageType.Hint); + break; + } + } + } + internal void BroadcastSystemMessage(Func messageSupplier) + { + foreach (UserConnection con in UserConnectionService.ActiveConnections) + { + switch (con.Stage) + { + case GameStage.Game: + case GameStage.Observer: + con.ReceiveChat(messageSupplier.Invoke(con), MessageType.System); + break; + default: + continue; + } + } + } + + internal void BroadcastMessage(string text, List linkedItems, MessageType messageType, Predicate shouldReceive) + { + foreach (UserConnection con in UserConnectionService.ActiveConnections) + { + switch (con.Stage) + { + case GameStage.Game: + case GameStage.Observer: + if (!shouldReceive.Invoke(con)) continue; + + con.ReceiveChat(text, messageType, linkedItems); + break; + default: continue; + } + } + } + } +} diff --git a/ServerLibrary/Models/ConquestWar.cs b/ServerLibrary/Models/ConquestWar.cs index 15837798..d3f4c3b0 100644 --- a/ServerLibrary/Models/ConquestWar.cs +++ b/ServerLibrary/Models/ConquestWar.cs @@ -2,6 +2,7 @@ using Library.SystemModels; using Server.DBModels; using Server.Envir; +using Server.Infrastructure.Network; using Server.Models.Monsters; using System; using System.Collections.Generic; @@ -23,12 +24,10 @@ public sealed class ConquestWar public Dictionary Stats = new Dictionary(); + public void StartWar() { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestStarted, Castle.Name), MessageType.System); - - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestStarted, Castle.Name)); Map = SEnvir.GetMap(Castle.Map); for (int i = Map.NPCs.Count - 1; i >= 0; i--) @@ -62,8 +61,7 @@ public void Process() public void EndWar() { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestFinished, Castle.Name), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestFinished, Castle.Name)); Ended = true; @@ -88,8 +86,7 @@ public void EndWar() if (ownerGuild != null) { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestOwner, ownerGuild.GuildName, Castle.Name), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestOwner, ownerGuild.GuildName, Castle.Name)); UserConquest conquest = SEnvir.UserConquestList.Binding.FirstOrDefault(x => x.Castle == Castle && x.Castle == ownerGuild?.Castle); diff --git a/ServerLibrary/Models/Map.cs b/ServerLibrary/Models/Map.cs index 22c9a239..6f82bc2c 100644 --- a/ServerLibrary/Models/Map.cs +++ b/ServerLibrary/Models/Map.cs @@ -3,6 +3,7 @@ using Library.SystemModels; using Server.DBModels; using Server.Envir; +using Server.Infrastructure.Network; using Server.Models.Monsters; using System; using System.Collections.Generic; @@ -60,7 +61,7 @@ public void Load() if (!File.Exists(path)) { - SEnvir.Log($"Map: {path} not found."); + SEnvir.ServerLogger.Log($"Map: {path} not found."); return; } @@ -106,7 +107,7 @@ private void CreateGuards() { if (info.Monster == null) { - SEnvir.Log($"Failed to spawn Unset Guard Map:{Info.Description}, Location: {info.X}, {info.Y}"); + SEnvir.ServerLogger.Log($"Failed to spawn Unset Guard Map:{Info.Description}, Location: {info.X}, {info.Y}"); continue; } @@ -115,7 +116,7 @@ private void CreateGuards() if (!mob.Spawn(this, new Point(info.X, info.Y))) { - SEnvir.Log($"Failed to spawn Guard Map:{Info.Description}, Location: {info.X}, {info.Y}"); + SEnvir.ServerLogger.Log($"Failed to spawn Guard Map:{Info.Description}, Location: {info.X}, {info.Y}"); continue; } } @@ -133,7 +134,7 @@ private void CreateCastleFlags() if (!mob.Spawn(castle, info)) { - SEnvir.Log($"Failed to spawn Flag Map:{Info.Description}, Location: {info.X}, {info.Y}"); + SEnvir.ServerLogger.Log($"Failed to spawn Flag Map:{Info.Description}, Location: {info.X}, {info.Y}"); continue; } } @@ -196,7 +197,7 @@ public void CreateCellRegions() if (source == null) { - SEnvir.Log($"[Cell] Bad Point, Source: {Info.FileName} {region.Description}, X:{sPoint.X}, Y:{sPoint.Y}"); + SEnvir.ServerLogger.Log($"[Cell] Bad Point, Source: {Info.FileName} {region.Description}, X:{sPoint.X}, Y:{sPoint.Y}"); continue; } @@ -442,13 +443,11 @@ public void DoSpawn(bool eventSpawn) { if (Info.Delay >= 1000000) { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat($"{mob.MonsterInfo.MonsterName} has appeared.", MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => $"{mob.MonsterInfo.MonsterName} has appeared."); } else { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.BossSpawn, CurrentMap.Info.Description), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.BossSpawn, CurrentMap.Info.Description)); } } diff --git a/ServerLibrary/Models/Monsters/CastleFlag.cs b/ServerLibrary/Models/Monsters/CastleFlag.cs index b66c6917..e1c15833 100644 --- a/ServerLibrary/Models/Monsters/CastleFlag.cs +++ b/ServerLibrary/Models/Monsters/CastleFlag.cs @@ -3,6 +3,7 @@ using Library.SystemModels; using Server.DBModels; using Server.Envir; +using Server.Infrastructure.Network; using System; using System.Drawing; using System.Linq; @@ -71,9 +72,7 @@ public override void Process() if (Target == null && Contester != null) { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestNotTakingFlag, Contester.GuildName, War.Castle.Name), MessageType.System); - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestNotTakingFlag, Contester.GuildName, War.Castle.Name)); Contester = null; ContesterTime = DateTime.MaxValue; } @@ -127,10 +126,7 @@ protected override void Attack() //Start 30 seconds timer ContesterTime = SEnvir.Now.Add(ContesterDelay); - - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestTakingFlag, Contester.GuildName, War.Castle.Name, _takeDuration), MessageType.System); - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestTakingFlag, Contester.GuildName, War.Castle.Name, _takeDuration)); return; } else @@ -154,9 +150,7 @@ protected override void Attack() { //Another guild near flag - reset contest time ContesterTime = SEnvir.Now.Add(ContesterDelay); - - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestPreventingFlag, player.Character.Account.GuildMember.Guild.GuildName, Contester.GuildName, War.Castle.Name), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestPreventingFlag, player.Character.Account.GuildMember.Guild.GuildName, Contester.GuildName, War.Castle.Name)); return; } @@ -168,8 +162,7 @@ protected override void Attack() if (!contestGuildNear) { - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestNotTakingFlag, Contester.GuildName, War.Castle.Name), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestNotTakingFlag, Contester.GuildName, War.Castle.Name)); Contester = null; ContesterTime = DateTime.MaxValue; @@ -179,9 +172,7 @@ protected override void Attack() else { var difference = (ContesterTime - SEnvir.Now).Seconds; - - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestTakingFlag, Contester.GuildName, War.Castle.Name, difference), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestTakingFlag, Contester.GuildName, War.Castle.Name, difference)); } } @@ -196,9 +187,7 @@ protected override void Attack() //Update new guild with castle Contester.Castle = War.Castle; - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestCapture, Contester.GuildName, War.Castle.Name), MessageType.System); - + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestCapture, Contester.GuildName, War.Castle.Name)); SEnvir.Broadcast(new S.GuildCastleInfo { Index = War.Castle.Index, Owner = Contester.GuildName }); Contester = null; diff --git a/ServerLibrary/Models/Monsters/CastleGate.cs b/ServerLibrary/Models/Monsters/CastleGate.cs index 0aafd55d..0291c205 100644 --- a/ServerLibrary/Models/Monsters/CastleGate.cs +++ b/ServerLibrary/Models/Monsters/CastleGate.cs @@ -125,7 +125,7 @@ private void SpawnBlockers() if (!b.Spawn(this.CurrentMap, new Point(this.CurrentLocation.X + block.X, this.CurrentLocation.Y + block.Y))) { - SEnvir.Log(string.Format("{3} blocking mob not spawned at {0} {1}:{2}", CurrentMap.Info.FileName, block.X, block.Y, MonsterInfo.MonsterName)); + SEnvir.ServerLogger.Log(string.Format("{3} blocking mob not spawned at {0} {1}:{2}", CurrentMap.Info.FileName, block.X, block.Y, MonsterInfo.MonsterName)); } } } diff --git a/ServerLibrary/Models/Monsters/CastleLord.cs b/ServerLibrary/Models/Monsters/CastleLord.cs index 19220ede..68c5e544 100644 --- a/ServerLibrary/Models/Monsters/CastleLord.cs +++ b/ServerLibrary/Models/Monsters/CastleLord.cs @@ -1,6 +1,7 @@ using Library; using Server.DBModels; using Server.Envir; +using Server.Infrastructure.Network; using System; using System.Collections.Generic; using System.Drawing; @@ -280,9 +281,8 @@ public override void Die() EXPOwner.Character.Account.GuildMember.Guild.Castle = War.Castle; - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.ConquestCapture, EXPOwner.Character.Account.GuildMember.Guild.GuildName, War.Castle.Name), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.ConquestCapture, EXPOwner.Character.Account.GuildMember.Guild.GuildName, War.Castle.Name)); SEnvir.Broadcast(new S.GuildCastleInfo { Index = War.Castle.Index, Owner = EXPOwner.Character.Account.GuildMember.Guild.GuildName }); War.CastleTarget = null; diff --git a/ServerLibrary/Models/Monsters/Companion.cs b/ServerLibrary/Models/Monsters/Companion.cs index 23e73f28..74dfc9c9 100644 --- a/ServerLibrary/Models/Monsters/Companion.cs +++ b/ServerLibrary/Models/Monsters/Companion.cs @@ -52,7 +52,7 @@ public Companion(UserCompanion companion) if (item.Slot - Globals.EquipmentOffSet >= Equipment.Length) { - SEnvir.Log($"[Bag Companion Equipment] Slot: {item.Slot}, Character: {UserCompanion.Character.CharacterName}, Companion: {UserCompanion.Name}"); + SEnvir.ServerLogger.Log($"[Bag Companion Equipment] Slot: {item.Slot}, Character: {UserCompanion.Character.CharacterName}, Companion: {UserCompanion.Name}"); continue; } @@ -76,7 +76,7 @@ public Companion(UserCompanion companion) if (item.Slot >= Inventory.Length) { - SEnvir.Log($"[Bag Companion Inventory] Slot: {item.Slot}, Character: {UserCompanion.Character.CharacterName}, Companion: {UserCompanion.Name}"); + SEnvir.ServerLogger.Log($"[Bag Companion Inventory] Slot: {item.Slot}, Character: {UserCompanion.Character.CharacterName}, Companion: {UserCompanion.Name}"); continue; } diff --git a/ServerLibrary/Models/Monsters/JinamStoneGate.cs b/ServerLibrary/Models/Monsters/JinamStoneGate.cs index 012e2df6..f09a42ba 100644 --- a/ServerLibrary/Models/Monsters/JinamStoneGate.cs +++ b/ServerLibrary/Models/Monsters/JinamStoneGate.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Library; using Server.Envir; +using Server.Infrastructure.Network; namespace Server.Models.Monsters { @@ -16,7 +17,6 @@ public class JinamStoneGate :MonsterObject public DateTime DespawnTime; - public JinamStoneGate() { Direction = MirDirection.Up; @@ -31,13 +31,9 @@ protected override void OnSpawned() { base.OnSpawned(); - DespawnTime = SEnvir.Now.AddMinutes(20); - - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.LairGateOpen, CurrentMap.Info.Description, CurrentLocation), MessageType.System); - - } + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.LairGateOpen, CurrentMap.Info.Description, CurrentLocation)); + } public override void Process() { @@ -48,8 +44,7 @@ public override void Process() if (SpawnInfo != null) SpawnInfo.AliveCount--; - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(con.Language.LairGateClosed, MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => c.Language.LairGateClosed); SpawnInfo = null; Despawn(); diff --git a/ServerLibrary/Models/Monsters/NetherworldGate.cs b/ServerLibrary/Models/Monsters/NetherworldGate.cs index 4c8f5ad5..3a2bb9cc 100644 --- a/ServerLibrary/Models/Monsters/NetherworldGate.cs +++ b/ServerLibrary/Models/Monsters/NetherworldGate.cs @@ -2,6 +2,7 @@ using System.Drawing; using Library; using Server.Envir; +using Server.Infrastructure.Network; namespace Server.Models.Monsters { @@ -11,7 +12,6 @@ public class NetherworldGate : MonsterObject public override bool CanAttack => false; public DateTime DespawnTime; - public NetherworldGate() { @@ -28,9 +28,7 @@ protected override void OnSpawned() base.OnSpawned(); DespawnTime = SEnvir.Now.AddMinutes(20); - - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(string.Format(con.Language.NetherGateOpen, CurrentMap.Info.Description, CurrentLocation), MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => string.Format(c.Language.NetherGateOpen, CurrentMap.Info.Description, CurrentLocation)); } public override void Process() @@ -42,8 +40,7 @@ public override void Process() if (SpawnInfo != null) SpawnInfo.AliveCount--; - foreach (SConnection con in SEnvir.Connections) - con.ReceiveChat(con.Language.NetherGateClosed, MessageType.System); + SEnvir.BroadcastService.BroadcastSystemMessage(c => c.Language.NetherGateClosed); SpawnInfo = null; Despawn(); diff --git a/ServerLibrary/Models/PlayerObject.cs b/ServerLibrary/Models/PlayerObject.cs index 868407c4..22afa044 100644 --- a/ServerLibrary/Models/PlayerObject.cs +++ b/ServerLibrary/Models/PlayerObject.cs @@ -5,6 +5,7 @@ using Server.DBModels; using Server.Envir; using Server.Envir.Events.Triggers; +using Server.Infrastructure.Service.Connection; using Server.Models.Magics; using Server.Models.Monsters; using System; @@ -24,7 +25,7 @@ public sealed class PlayerObject : MapObject public override ObjectType Race => ObjectType.Player; public CharacterInfo Character; - public SConnection Connection; + public UserConnection Connection; public override string Name { @@ -179,7 +180,7 @@ public UserItem[] public Point FishingLocation; public MirDirection FishingDirection; - public PlayerObject(CharacterInfo info, SConnection con) + public PlayerObject(CharacterInfo info, UserConnection con) { Character = info; Connection = con; @@ -874,7 +875,7 @@ public void StartGame() { if (!SetBindPoint()) { - SEnvir.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}, Failed to reset bind point."); + SEnvir.ServerLogger.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}, Failed to reset bind point."); Enqueue(new S.StartGame { Result = StartGameResult.UnableToSpawn }); Connection = null; Character = null; @@ -903,7 +904,7 @@ public void StartGame() } else { - SEnvir.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}"); + SEnvir.ServerLogger.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}"); Enqueue(new S.StartGame { Result = StartGameResult.UnableToSpawn }); Connection = null; Character = null; @@ -912,7 +913,7 @@ public void StartGame() } else if (!Spawn(Character.CurrentMap, null, 0, CurrentLocation) && !Spawn(Character.BindPoint.BindRegion, null, 0)) { - SEnvir.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}"); + SEnvir.ServerLogger.Log($"[Failed to spawn Character] Index: {Character.Index}, Name: {Character.CharacterName}"); Enqueue(new S.StartGame { Result = StartGameResult.UnableToSpawn }); Connection = null; Character = null; @@ -1110,7 +1111,7 @@ protected override void OnSpawned() Enqueue(new S.FortuneUpdate { Fortunes = Character.Account.Fortunes.Select(x => x.ToClientInfo()).ToList() }); } - public void SetUpObserver(SConnection con) + public void SetUpObserver(UserConnection con) { con.Stage = GameStage.Observer; con.Observed = Connection; @@ -1455,7 +1456,7 @@ public void RemoveMount() public void Chat(string text) { if (string.IsNullOrEmpty(text)) return; - SEnvir.LogChat($"{Name}: {text}"); + SEnvir.UserChatLogger.Log($"{Name}: {text}"); //Item Links @@ -1493,7 +1494,7 @@ public void Chat(string text) if (parts.Length == 0) return; - SConnection con = SEnvir.GetConnectionByCharacter(parts[0]); + UserConnection con = SEnvir.GetConnectionByCharacter(parts[0]); if (con == null || (con.Stage != GameStage.Observer && con.Stage != GameStage.Game) || SEnvir.IsBlocking(Character.Account, con.Account)) { @@ -1569,20 +1570,8 @@ public void Chat(string text) } text = string.Format("(!@){0}: {1}", Name, text.Remove(0, 2)); + SEnvir.BroadcastService.BroadcastMessage(text, linkedItems, MessageType.Global, c => !SEnvir.IsBlocking(Character.Account, c.Account)); - foreach (SConnection con in SEnvir.Connections) - { - switch (con.Stage) - { - case GameStage.Game: - case GameStage.Observer: - if (SEnvir.IsBlocking(Character.Account, con.Account)) continue; - - con.ReceiveChat(text, MessageType.Global, linkedItems); - break; - default: continue; - } - } } else if (text.StartsWith("!")) { @@ -1611,7 +1600,7 @@ public void Chat(string text) if (!SEnvir.IsBlocking(Character.Account, player.Character.Account)) player.Connection.ReceiveChat(text, MessageType.Shout, linkedItems); - foreach (SConnection observer in player.Connection.Observers) + foreach (UserConnection observer in player.Connection.Observers) { if (SEnvir.IsBlocking(Character.Account, observer.Account)) continue; @@ -1624,18 +1613,7 @@ public void Chat(string text) if (!Character.Account.TempAdmin) return; text = string.Format("{0}: {1}", Name, text.Remove(0, 2)); - - foreach (SConnection con in SEnvir.Connections) - { - switch (con.Stage) - { - case GameStage.Game: - case GameStage.Observer: - con.ReceiveChat(text, MessageType.Announcement, linkedItems); - break; - default: continue; - } - } + SEnvir.BroadcastService.BroadcastMessage(text, linkedItems, MessageType.Announcement, c => true); } else if (text.StartsWith("@")) { @@ -1652,7 +1630,7 @@ public void Chat(string text) Connection.ReceiveChat(text, MessageType.ObserverChat, linkedItems); - foreach (SConnection target in Connection.Observers) + foreach (UserConnection target in Connection.Observers) { if (SEnvir.IsBlocking(Character.Account, target.Account)) continue; @@ -1671,7 +1649,7 @@ public void Chat(string text) if (!SEnvir.IsBlocking(Character.Account, player.Character.Account)) player.Connection.ReceiveChat(text, MessageType.Normal, linkedItems, ObjectID); - foreach (SConnection observer in player.Connection.Observers) + foreach (UserConnection observer in player.Connection.Observers) { if (SEnvir.IsBlocking(Character.Account, observer.Account)) continue; @@ -1680,7 +1658,7 @@ public void Chat(string text) } } } - public void ObserverChat(SConnection con, string text) + public void ObserverChat(UserConnection con, string text) { if (string.IsNullOrEmpty(text)) return; @@ -1689,7 +1667,7 @@ public void ObserverChat(SConnection con, string text) con.ReceiveChat(con.Language.ObserverNotLoggedIn, MessageType.System); return; } - SEnvir.LogChat($"{con.Account.LastCharacter.CharacterName}: {text}"); + SEnvir.UserChatLogger.Log($"{con.Account.LastCharacter.CharacterName}: {text}"); string[] parts; @@ -1701,7 +1679,7 @@ public void ObserverChat(SConnection con, string text) if (parts.Length == 0) return; - SConnection target = SEnvir.GetConnectionByCharacter(parts[0]); + UserConnection target = SEnvir.GetConnectionByCharacter(parts[0]); if (target == null || (target.Stage != GameStage.Observer && target.Stage != GameStage.Game) || SEnvir.IsBlocking(con.Account, target.Account)) { @@ -1760,38 +1738,14 @@ public void ObserverChat(SConnection con, string text) } text = string.Format("(!@){0}: {1}", con.Account.LastCharacter.CharacterName, text.Remove(0, 2)); - - foreach (SConnection target in SEnvir.Connections) - { - switch (target.Stage) - { - case GameStage.Game: - case GameStage.Observer: - if (SEnvir.IsBlocking(con.Account, target.Account)) continue; - - target.ReceiveChat(text, MessageType.Global); - break; - default: continue; - } - } + SEnvir.BroadcastService.BroadcastMessage(text, null, MessageType.Global, c => SEnvir.IsBlocking(con.Account, c.Account)); } else if (text.StartsWith("@!")) { if (!con.Account.LastCharacter.Account.TempAdmin) return; text = string.Format("{0}: {1}", con.Account.LastCharacter.CharacterName, text.Remove(0, 2)); - - foreach (SConnection target in SEnvir.Connections) - { - switch (target.Stage) - { - case GameStage.Game: - case GameStage.Observer: - target.ReceiveChat(text, MessageType.Announcement); - break; - default: continue; - } - } + SEnvir.BroadcastService.BroadcastMessage(text, null, MessageType.Announcement, c => true); } else { @@ -1801,7 +1755,7 @@ public void ObserverChat(SConnection con, string text) Connection.ReceiveChat(text, MessageType.ObserverChat); - foreach (SConnection target in Connection.Observers) + foreach (UserConnection target in Connection.Observers) { if (SEnvir.IsBlocking(con.Account, target.Account)) continue; @@ -1810,7 +1764,7 @@ public void ObserverChat(SConnection con, string text) } } - public void Inspect(int index, bool ranking, SConnection con) + public void Inspect(int index, bool ranking, UserConnection con) { //if (index == Character.Index) return; @@ -1885,7 +1839,7 @@ public override void ItemRevive() UpdateReviveTimers(Connection); } - public void UpdateReviveTimers(SConnection con) + public void UpdateReviveTimers(UserConnection con) { con.Enqueue(new S.ReviveTimers { @@ -6034,18 +5988,7 @@ public void ItemUse(CellLinkInfo link) string text = $"A [{item.Info.ItemName}] has been used in {CurrentMap.Info.Description}"; - - foreach (SConnection con in SEnvir.Connections) - { - switch (con.Stage) - { - case GameStage.Game: - case GameStage.Observer: - con.ReceiveChat(text, MessageType.System); - break; - default: continue; - } - } + SEnvir.BroadcastService.BroadcastMessage(text, null, MessageType.System, c => true); } } } @@ -6898,7 +6841,7 @@ public void ItemMove(C.ItemMove p) } else { - foreach (SConnection con in Connection.Observers) + foreach (UserConnection con in Connection.Observers) { con.Enqueue(new S.ItemChanged { @@ -6930,7 +6873,7 @@ public void ItemMove(C.ItemMove p) } else { - foreach (SConnection con in Connection.Observers) + foreach (UserConnection con in Connection.Observers) { con.Enqueue(new S.ItemChanged { @@ -7014,7 +6957,7 @@ public void ItemMove(C.ItemMove p) { //Sendto MY observers I got item from guild store and what slot? - foreach (SConnection con in Connection.Observers) + foreach (UserConnection con in Connection.Observers) { con.Enqueue(new S.GuildGetItem { @@ -7084,7 +7027,7 @@ public void ItemMove(C.ItemMove p) if (p.FromGrid == GridType.GuildStorage) break; //Already Handled //Must be removing from player to GuildStorage, Update Observer's bag - foreach (SConnection con in Connection.Observers) + foreach (UserConnection con in Connection.Observers) { con.Enqueue(new S.ItemChanged { @@ -8322,7 +8265,7 @@ public void NameChange(string newName) result.Link.Count = 0; } - SEnvir.Log($"[NAME CHANGED] Old: {Name}, New: {newName}.", true); + SEnvir.ServerLogger.Log($"[NAME CHANGED] Old: {Name}, New: {newName}."); Name = newName; SendChangeUpdate(); @@ -8368,7 +8311,7 @@ public void CaptionChange(string newCaption) } Character.Caption = newCaption; Caption = newCaption; - SEnvir.Log($"[CAPTION CHANGED] {Character.CharacterName} caption changed to: {Caption}", true); + SEnvir.ServerLogger.Log($"[CAPTION CHANGED] {Character.CharacterName} caption changed to: {Caption}"); Connection.ReceiveChat($"Your caption changed to: {Caption}.", MessageType.System); @@ -13343,7 +13286,7 @@ public void Attack(MirDirection direction, MagicType attackMagic) if (attackMagic != validMagic) { - SEnvir.Log($"[ERROR] {Name} requested Attack Skill '{attackMagic}' but valid magic was '{validMagic}'."); + SEnvir.ServerLogger.Log($"[ERROR] {Name} requested Attack Skill '{attackMagic}' but valid magic was '{validMagic}'."); Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation }); return; } diff --git a/ServerLibrary/Models/SpellObject.cs b/ServerLibrary/Models/SpellObject.cs index 22087821..f215463d 100644 --- a/ServerLibrary/Models/SpellObject.cs +++ b/ServerLibrary/Models/SpellObject.cs @@ -97,13 +97,13 @@ public override void Process() if (CurrentCell == null) { - SEnvir.Log($"[ERROR] {Effect} CurrentCell Null."); + SEnvir.ServerLogger.Log($"[ERROR] {Effect} CurrentCell Null."); return; } if (CurrentCell.Objects == null) { - SEnvir.Log($"[ERROR] {Effect} CurrentCell.Objects Null."); + SEnvir.ServerLogger.Log($"[ERROR] {Effect} CurrentCell.Objects Null."); return; } @@ -116,13 +116,13 @@ public override void Process() if (CurrentCell == null) { - SEnvir.Log($"[ERROR] {Effect} CurrentCell Null Loop."); + SEnvir.ServerLogger.Log($"[ERROR] {Effect} CurrentCell Null Loop."); return; } if (CurrentCell.Objects == null) { - SEnvir.Log($"[ERROR] {Effect} CurrentCell.Objects Null Loop."); + SEnvir.ServerLogger.Log($"[ERROR] {Effect} CurrentCell.Objects Null Loop."); return; }