From 6d80785dfd09decf98d45dcff9f185c2e43588c0 Mon Sep 17 00:00:00 2001 From: Aaron Yarborough Date: Wed, 11 Sep 2024 21:38:32 +0100 Subject: [PATCH] refactor --- GServer.Client/Program.cs | 144 +++++++++--------- GServer.Common/Extensions/StringExtensions.cs | 10 +- GServer.Common/Game/Entities/ServerListing.cs | 2 +- .../Networking/Core/MessageMemoryStream.cs | 6 +- .../Networking/Enums/ClientPacketIn.cs | 6 +- .../Networking/Enums/ServerPacketIn.cs | 4 +- .../Networking/Messages/BaseMessage.cs | 2 +- .../Messages/Client/AuthResponseMessage.cs | 137 ++++++++--------- .../Networking/Messages/IMessageHandler.cs | 1 + .../Networking/Messages/Server/AuthMessage.cs | 13 +- .../Messages/Server/ListServersMessage.cs | 2 +- GServer.NCSServer/GServer.NCSServer.csproj | 13 ++ GServer.NCSServer/GameSession.cs | 51 +++++++ GServer.NCSServer/Program.cs | 4 + .../Properties/launchSettings.json | 38 +++++ .../appsettings.Development.json | 8 + GServer.NCSServer/appsettings.json | 9 ++ GServer.Server/GServer.Server.csproj | 4 +- GServer.Server/GameServerOptions.cs | 1 - GServer.Server/Program.cs | 16 +- GServer.Server/Services/ServerListService.cs | 24 --- GServer.Server/TCPGameServer.cs | 94 ------------ GServer.Server/TcpGameServer.cs | 75 +++++++++ ...MessageHandler.cs => TcpMessageHandler.cs} | 16 +- GServer.Server/UDPGameServer.cs | 74 --------- GServer.sln | 6 + 26 files changed, 389 insertions(+), 371 deletions(-) create mode 100644 GServer.NCSServer/GServer.NCSServer.csproj create mode 100644 GServer.NCSServer/GameSession.cs create mode 100644 GServer.NCSServer/Program.cs create mode 100644 GServer.NCSServer/Properties/launchSettings.json create mode 100644 GServer.NCSServer/appsettings.Development.json create mode 100644 GServer.NCSServer/appsettings.json delete mode 100644 GServer.Server/Services/ServerListService.cs delete mode 100644 GServer.Server/TCPGameServer.cs create mode 100644 GServer.Server/TcpGameServer.cs rename GServer.Server/{TCPMessageHandler.cs => TcpMessageHandler.cs} (73%) delete mode 100644 GServer.Server/UDPGameServer.cs diff --git a/GServer.Client/Program.cs b/GServer.Client/Program.cs index 5b5605c..fbc0626 100644 --- a/GServer.Client/Program.cs +++ b/GServer.Client/Program.cs @@ -1,73 +1,73 @@ -using System.Net; -using System.Net.Sockets; -using GServer.Common; -using GServer.Common.Networking.Enums; -using GServer.Common.Networking.Messages.Client; -using GServer.Common.Networking.Messages.Server; - -internal class Program -{ - private const int SERVER_PORT = 11000; - - private static void Main(string[] args) - { - IPEndPoint serverEP = new(IPAddress.Any, SERVER_PORT); - - TcpClient tcpClient = new(); - tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - tcpClient.Connect(serverEP); - - Console.WriteLine("Username..."); - string username = Console.ReadLine()!; - - Console.WriteLine("Password..."); - string password = Console.ReadLine()!; - - AuthMessage authMessage = new(username, password); - _ = tcpClient.Client.Send(authMessage.Serialize()); - - try - { - while (true) - { - byte[] bytes = new byte[tcpClient.Client.ReceiveBufferSize]; - _ = tcpClient.Client.Receive(bytes); - - MessageMemoryStream stream = new(bytes); - - ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte(); - switch (packetIn) - { - case ClientPacketIn.AUTH_RESPONSE: - AuthResponseMessage authResultMessage = new(stream); - - Console.WriteLine("Success = " + authResultMessage.IsSuccessful); - Console.WriteLine("SessionToken = " + authResultMessage.SessionToken ?? "null"); - Console.WriteLine("FailureReason = " + authResultMessage.FailureReason ?? "null"); - - break; - - case ClientPacketIn.LIST_SERVERS_RESPONSE: - break; - - case ClientPacketIn.UNKNOWN: - break; - - default: - Console.WriteLine($"Received unsupported packet."); - break; - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - finally - { - tcpClient.Close(); - } - - - } +using System.Net; +using System.Net.Sockets; +using GServer.Common.Networking.Core; +using GServer.Common.Networking.Enums; +using GServer.Common.Networking.Messages.Client; +using GServer.Common.Networking.Messages.Server; + +namespace GServer.Client; + +public class Program +{ + private const int ServerPort = 11000; + + private static void Main(string[] args) + { + IPEndPoint serverEp = new(IPAddress.Any, ServerPort); + + TcpClient tcpClient = new(); + tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + tcpClient.Connect(serverEp); + + Console.WriteLine("Username..."); + string username = Console.ReadLine()!; + + Console.WriteLine("Password..."); + string password = Console.ReadLine()!; + + AuthMessage authMessage = new(username, password); + _ = tcpClient.Client.Send(authMessage.Serialize()); + + try + { + while (true) + { + byte[] bytes = new byte[tcpClient.Client.ReceiveBufferSize]; + _ = tcpClient.Client.Receive(bytes); + + MessageMemoryStream stream = new(bytes); + + ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte(); + switch (packetIn) + { + case ClientPacketIn.AuthResponse: + AuthResponseMessage authResultMessage = new(stream); + + Console.WriteLine("Success = " + authResultMessage.IsSuccessful); + Console.WriteLine("SessionToken = " + authResultMessage.SessionToken); + Console.WriteLine("FailureReason = " + authResultMessage.FailureReason); + + break; + + case ClientPacketIn.ListServersResponse: + break; + + case ClientPacketIn.Unknown: + break; + + default: + Console.WriteLine("Received unsupported packet."); + break; + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + finally + { + tcpClient.Close(); + } + } } \ No newline at end of file diff --git a/GServer.Common/Extensions/StringExtensions.cs b/GServer.Common/Extensions/StringExtensions.cs index 60f35fe..dda135d 100644 --- a/GServer.Common/Extensions/StringExtensions.cs +++ b/GServer.Common/Extensions/StringExtensions.cs @@ -1,11 +1,11 @@ using System.Text; -namespace GServer.Common; +namespace GServer.Common.Extensions; public static class StringExtensions { - public static byte[] GetASCIIBytes(this string value) - { - return Encoding.ASCII.GetBytes(value); - } + public static byte[] GetAsciiBytes(this string value) + { + return Encoding.ASCII.GetBytes(value); + } } diff --git a/GServer.Common/Game/Entities/ServerListing.cs b/GServer.Common/Game/Entities/ServerListing.cs index cbd982e..d56a54a 100644 --- a/GServer.Common/Game/Entities/ServerListing.cs +++ b/GServer.Common/Game/Entities/ServerListing.cs @@ -5,7 +5,7 @@ public record ServerListing public required string Name { get; set; } public required string Description { get; set; } public ushort Playercount { get; set; } - public required string IPAddress { get; set; } + public required string IpAddress { get; set; } public ushort Port { get; set; } public ServerTier ServerTier { get; set; } } diff --git a/GServer.Common/Networking/Core/MessageMemoryStream.cs b/GServer.Common/Networking/Core/MessageMemoryStream.cs index b2ea9b8..15204b4 100644 --- a/GServer.Common/Networking/Core/MessageMemoryStream.cs +++ b/GServer.Common/Networking/Core/MessageMemoryStream.cs @@ -1,6 +1,6 @@ using System.Text; -namespace GServer.Common; +namespace GServer.Common.Networking.Core; public class MessageMemoryStream : MemoryStream { @@ -31,7 +31,7 @@ public class MessageMemoryStream : MemoryStream return BitConverter.ToInt16(buffer); } - public string ReadUTF8String(int length) + public string ReadUtf8String(int length) { byte[] bytes = new byte[length]; _ = Read(bytes, 0, length); @@ -49,7 +49,7 @@ public class MessageMemoryStream : MemoryStream Write(bytes, 0, 2); } - public void WriteUTF8String(string value) + public void WriteUtf8String(string value) { byte[] bytes = Encoding.UTF8.GetBytes(value); Write(bytes, 0, bytes.Length); diff --git a/GServer.Common/Networking/Enums/ClientPacketIn.cs b/GServer.Common/Networking/Enums/ClientPacketIn.cs index dc27e8c..e0ab067 100644 --- a/GServer.Common/Networking/Enums/ClientPacketIn.cs +++ b/GServer.Common/Networking/Enums/ClientPacketIn.cs @@ -5,12 +5,12 @@ public enum ClientPacketIn : byte /// /// Represents an auth result from the server. /// - AUTH_RESPONSE = 1, + AuthResponse = 1, /// /// Contains a list of server listings. /// - LIST_SERVERS_RESPONSE = 2, + ListServersResponse = 2, - UNKNOWN = 255 + Unknown = 255 } \ No newline at end of file diff --git a/GServer.Common/Networking/Enums/ServerPacketIn.cs b/GServer.Common/Networking/Enums/ServerPacketIn.cs index 2e710a5..8a46832 100644 --- a/GServer.Common/Networking/Enums/ServerPacketIn.cs +++ b/GServer.Common/Networking/Enums/ServerPacketIn.cs @@ -2,6 +2,6 @@ namespace GServer.Common.Networking.Enums; public enum ServerPacketIn : byte { - AUTH = 1, - LIST_SERVERS = 2 + Auth = 1, + ListServers = 2 } \ No newline at end of file diff --git a/GServer.Common/Networking/Messages/BaseMessage.cs b/GServer.Common/Networking/Messages/BaseMessage.cs index 7289d45..91ebde1 100644 --- a/GServer.Common/Networking/Messages/BaseMessage.cs +++ b/GServer.Common/Networking/Messages/BaseMessage.cs @@ -4,7 +4,7 @@ public abstract class BaseMessage { protected readonly byte PacketId; - public BaseMessage(byte packetId) + protected BaseMessage(byte packetId) { PacketId = packetId; } diff --git a/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs b/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs index ad69d4d..3d4668d 100644 --- a/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs +++ b/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs @@ -1,68 +1,69 @@ -using System.Text; -using GServer.Common.Networking.Enums; - -namespace GServer.Common.Networking.Messages.Client; - -public enum AuthResponseFailure : byte -{ - IncorrectLoginOrPassword, - Unknown -} - -public class AuthResponseMessage : BaseMessage, IMessage -{ - public bool IsSuccessful { get; private set; } - - /// - /// Used to authenticate the user. Only set if IsSuccessful is true. - /// - public string? SessionToken { get; private set; } - - /// - /// Reason for auth failure. Only set is IsSuccessful is false. - /// - public AuthResponseFailure? FailureReason { get; private set; } - - public AuthResponseMessage(bool isSuccessful, string? sessionToken = null, AuthResponseFailure? failureReason = null) : base((byte)ClientPacketIn.AUTH_RESPONSE) - { - IsSuccessful = isSuccessful; - SessionToken = sessionToken; - FailureReason = failureReason; - } - - public AuthResponseMessage(MessageMemoryStream stream) : base((byte)ClientPacketIn.AUTH_RESPONSE) - { - IsSuccessful = stream.ReadBoolean(); - - if (IsSuccessful) - { - ushort sessionTokenLen = stream.ReadUInt16(); - SessionToken = stream.ReadUTF8String(sessionTokenLen); - } - else - { - FailureReason = (AuthResponseFailure)stream.ReadByte(); - } - } - - public byte[] Serialize() - { - using MessageMemoryStream stream = new(); - - stream.WriteByte(PacketId); - stream.WriteBoolean(IsSuccessful); - - if (IsSuccessful) - { - short sessionTokenByteLen = (short)Encoding.UTF8.GetByteCount(SessionToken!); - stream.WriteUInt16(sessionTokenByteLen); - stream.WriteUTF8String(SessionToken!); - } - else - { - stream.WriteByte((byte)FailureReason!); - } - - return stream.ToArray(); - } -} +using System.Text; +using GServer.Common.Networking.Core; +using GServer.Common.Networking.Enums; + +namespace GServer.Common.Networking.Messages.Client; + +public enum AuthResponseFailure : byte +{ + IncorrectLoginOrPassword, + Unknown +} + +public class AuthResponseMessage : BaseMessage, IMessage +{ + public bool IsSuccessful { get; } + + /// + /// Used to authenticate the user. Only set if IsSuccessful is true. + /// + public string? SessionToken { get; } + + /// + /// Reason for auth failure. Only set is IsSuccessful is false. + /// + public AuthResponseFailure? FailureReason { get; } + + public AuthResponseMessage(bool isSuccessful, string? sessionToken = null, AuthResponseFailure? failureReason = null) : base((byte)ClientPacketIn.AuthResponse) + { + IsSuccessful = isSuccessful; + SessionToken = sessionToken; + FailureReason = failureReason; + } + + public AuthResponseMessage(MessageMemoryStream stream) : base((byte)ClientPacketIn.AuthResponse) + { + IsSuccessful = stream.ReadBoolean(); + + if (IsSuccessful) + { + ushort sessionTokenLen = stream.ReadUInt16(); + SessionToken = stream.ReadUtf8String(sessionTokenLen); + } + else + { + FailureReason = (AuthResponseFailure)stream.ReadByte(); + } + } + + public byte[] Serialize() + { + using MessageMemoryStream stream = new(); + + stream.WriteByte(PacketId); + stream.WriteBoolean(IsSuccessful); + + if (IsSuccessful) + { + short sessionTokenByteLen = (short)Encoding.UTF8.GetByteCount(SessionToken!); + stream.WriteUInt16(sessionTokenByteLen); + stream.WriteUtf8String(SessionToken!); + } + else + { + stream.WriteByte((byte)FailureReason!); + } + + return stream.ToArray(); + } +} diff --git a/GServer.Common/Networking/Messages/IMessageHandler.cs b/GServer.Common/Networking/Messages/IMessageHandler.cs index 42e3866..0cb03d4 100644 --- a/GServer.Common/Networking/Messages/IMessageHandler.cs +++ b/GServer.Common/Networking/Messages/IMessageHandler.cs @@ -1,4 +1,5 @@ using System.Net.Sockets; +using GServer.Common.Networking.Core; namespace GServer.Common.Networking.Messages; diff --git a/GServer.Common/Networking/Messages/Server/AuthMessage.cs b/GServer.Common/Networking/Messages/Server/AuthMessage.cs index 0ad0750..fd5ad66 100644 --- a/GServer.Common/Networking/Messages/Server/AuthMessage.cs +++ b/GServer.Common/Networking/Messages/Server/AuthMessage.cs @@ -1,26 +1,27 @@ using System.Text; +using GServer.Common.Networking.Core; using GServer.Common.Networking.Enums; namespace GServer.Common.Networking.Messages.Server; public class AuthMessage : BaseMessage, IMessage { - public string Username { get; private set; } - public string Password { get; private set; } + public string Username { get; } + public string Password { get; } - public AuthMessage(string username, string password) : base((byte)ServerPacketIn.AUTH) + public AuthMessage(string username, string password) : base((byte)ServerPacketIn.Auth) { Username = username; Password = password; } - public AuthMessage(MessageMemoryStream stream) : base((byte)ServerPacketIn.AUTH) + public AuthMessage(MessageMemoryStream stream) : base((byte)ServerPacketIn.Auth) { byte usernameLen = (byte)stream.ReadByte(); - string username = stream.ReadUTF8String(usernameLen); + string username = stream.ReadUtf8String(usernameLen); byte passwordLen = (byte)stream.ReadByte(); - string password = stream.ReadUTF8String(passwordLen); + string password = stream.ReadUtf8String(passwordLen); Username = username; Password = password; diff --git a/GServer.Common/Networking/Messages/Server/ListServersMessage.cs b/GServer.Common/Networking/Messages/Server/ListServersMessage.cs index e9f394d..cec5bc5 100644 --- a/GServer.Common/Networking/Messages/Server/ListServersMessage.cs +++ b/GServer.Common/Networking/Messages/Server/ListServersMessage.cs @@ -4,7 +4,7 @@ namespace GServer.Common.Networking.Messages.Server; public class ListServersMessage : BaseMessage, IMessage { - public ListServersMessage() : base((byte)ServerPacketIn.LIST_SERVERS) + public ListServersMessage() : base((byte)ServerPacketIn.ListServers) { } diff --git a/GServer.NCSServer/GServer.NCSServer.csproj b/GServer.NCSServer/GServer.NCSServer.csproj new file mode 100644 index 0000000..ebc74e5 --- /dev/null +++ b/GServer.NCSServer/GServer.NCSServer.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/GServer.NCSServer/GameSession.cs b/GServer.NCSServer/GameSession.cs new file mode 100644 index 0000000..ca24f5a --- /dev/null +++ b/GServer.NCSServer/GameSession.cs @@ -0,0 +1,51 @@ +using System.Net.Sockets; +using NetCoreServer; + +namespace GServer.NCSServer; + +public class GameSession : TcpSession +{ + public GameSession(TcpServer server) : base(server) + { + } + + protected override void OnConnected() + { + Console.WriteLine($"Chat TCP session with Id {Id} connected!"); + } + + protected override void OnConnecting() + { + base.OnConnecting(); + } + + protected override void OnDisconnecting() + { + base.OnDisconnecting(); + } + + protected override void OnDisconnected() + { + Console.WriteLine($"Chat TCP session with Id {Id} disconnected!"); + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + base.OnReceived(buffer, offset, size); + } + + protected override void OnSent(long sent, long pending) + { + base.OnSent(sent, pending); + } + + protected override void OnEmpty() + { + base.OnEmpty(); + } + + protected override void OnError(SocketError error) + { + base.OnError(error); + } +} \ No newline at end of file diff --git a/GServer.NCSServer/Program.cs b/GServer.NCSServer/Program.cs new file mode 100644 index 0000000..d5d844b --- /dev/null +++ b/GServer.NCSServer/Program.cs @@ -0,0 +1,4 @@ +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +WebApplication app = builder.Build(); + +app.Run(); diff --git a/GServer.NCSServer/Properties/launchSettings.json b/GServer.NCSServer/Properties/launchSettings.json new file mode 100644 index 0000000..e5a5080 --- /dev/null +++ b/GServer.NCSServer/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:58673", + "sslPort": 44396 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5051", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7192;http://localhost:5051", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/GServer.NCSServer/appsettings.Development.json b/GServer.NCSServer/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/GServer.NCSServer/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/GServer.NCSServer/appsettings.json b/GServer.NCSServer/appsettings.json new file mode 100644 index 0000000..4d56694 --- /dev/null +++ b/GServer.NCSServer/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/GServer.Server/GServer.Server.csproj b/GServer.Server/GServer.Server.csproj index 8de6103..29b7a00 100644 --- a/GServer.Server/GServer.Server.csproj +++ b/GServer.Server/GServer.Server.csproj @@ -1,7 +1,7 @@  - - + + diff --git a/GServer.Server/GameServerOptions.cs b/GServer.Server/GameServerOptions.cs index 6dd047f..e9e2911 100644 --- a/GServer.Server/GameServerOptions.cs +++ b/GServer.Server/GameServerOptions.cs @@ -2,5 +2,4 @@ public class GameServerOptions { - public int PacketLength { get; set; } } diff --git a/GServer.Server/Program.cs b/GServer.Server/Program.cs index bbaadc9..3155efa 100644 --- a/GServer.Server/Program.cs +++ b/GServer.Server/Program.cs @@ -1,15 +1,23 @@ using System.Net; -using GServer.Server; +namespace GServer.Server; + +// ReSharper disable once ClassNeverInstantiated.Global internal sealed class Program { - private const int LISTEN_PORT = 11000; + private const int ListenPort = 11000; private static void Main(string[] args) { - TCPGameServer server = new(new IPEndPoint(IPAddress.Any, LISTEN_PORT), new GameServerOptions()); + CancellationTokenSource cancellationTokenSource = new(); - server.Start(cancellationTokenSource.Token).Wait(); + Thread serverWorker = new(delegate() + { + TcpGameServer server = new(new IPEndPoint(IPAddress.Any, ListenPort), new GameServerOptions()); + server.Start(); + }); + + serverWorker.Start(); } } \ No newline at end of file diff --git a/GServer.Server/Services/ServerListService.cs b/GServer.Server/Services/ServerListService.cs deleted file mode 100644 index f11b38d..0000000 --- a/GServer.Server/Services/ServerListService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GServer.Common.Game.Entities; - -namespace GServer.Server.Services; - -public class ServerListService -{ - public ServerListService() - { - } - - public IEnumerable List() - { - return [ - new ServerListing - { - Name = "Smallville", - Description = "A tiny development server!", - IPAddress = "localhost", - Port = 11001, - Playercount = 1, - } - ]; - } -} \ No newline at end of file diff --git a/GServer.Server/TCPGameServer.cs b/GServer.Server/TCPGameServer.cs deleted file mode 100644 index 3e49f81..0000000 --- a/GServer.Server/TCPGameServer.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Net; -using System.Net.Sockets; -using GServer.Common; -using GServer.Common.Networking.Messages; - -namespace GServer.Server; - -public class TCPGameServer : IDisposable -{ - private readonly TcpListener _tcpListener; - private readonly IPEndPoint _endPoint; - private readonly GameServerOptions _options; - private readonly IMessageHandler _messageHandler; - - public TCPGameServer(IPEndPoint endPoint, GameServerOptions options) - { - _endPoint = endPoint; - _options = options; - - _tcpListener = new TcpListener(endPoint); - _messageHandler = new TCPMessageHandler(); - } - - /// - /// Bind the server to the given endpoint. - /// - public async Task Start(CancellationToken cancellationToken) - { - Console.WriteLine("Starting TCPGameServer listener..."); - _tcpListener.Start(); - _ = cancellationToken.Register(_tcpListener.Stop); - Console.WriteLine($"TCPGameServer listening on {_endPoint}"); - - while (!cancellationToken.IsCancellationRequested) - { - try - { - using TcpClient tcpClient = await _tcpListener.AcceptTcpClientAsync(cancellationToken); - - await ProcessAsync(tcpClient); - - } - catch (SocketException) when (cancellationToken.IsCancellationRequested) - { - Console.WriteLine("TcpListener stopped listening because cancellation was requested."); - } - catch - { - throw; - } - finally - { - _tcpListener.Stop(); - } - } - - } - - /// - /// Begin processing messages - /// - /// - public async Task ProcessAsync(TcpClient tcpClient) - { - try - { - using NetworkStream stream = tcpClient.GetStream(); - - byte[] data = new byte[tcpClient.ReceiveBufferSize]; - int bytesRead = 0; - int chunkSize = 1; - - // Read everything into the data buffer - while (bytesRead < data.Length && chunkSize > 0) - { - bytesRead += - chunkSize = - await stream.ReadAsync(data.AsMemory(bytesRead, data.Length - bytesRead)); - } - - // Use the in-memory buffer to process the message - await _messageHandler.HandleMessageAsync(stream.Socket, new MessageMemoryStream(data)); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - public void Dispose() - { - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/GServer.Server/TcpGameServer.cs b/GServer.Server/TcpGameServer.cs new file mode 100644 index 0000000..326f4b7 --- /dev/null +++ b/GServer.Server/TcpGameServer.cs @@ -0,0 +1,75 @@ +using System.Net; +using System.Net.Sockets; +using GServer.Common.Networking.Core; +using GServer.Common.Networking.Messages; + +namespace GServer.Server; + +public class TcpGameServer(IPEndPoint endPoint, GameServerOptions options) : IDisposable +{ + private readonly GameServerOptions _options = options; + private readonly TcpListener _tcpListener = new(endPoint); + private readonly IMessageHandler _messageHandler = new TcpMessageHandler(); + + /// + /// Bind the server to the given endpoint. + /// + public void Start() + { + Console.WriteLine($"Starting ${nameof(TcpGameServer)} listener..."); + _tcpListener.Start(); + Console.WriteLine($"{nameof(TcpGameServer)} listening on {endPoint}"); + + while (true) + { + try + { + Console.WriteLine("Waiting for a connection..."); + + _tcpListener.AcceptTcpClient(); + Console.WriteLine("Client accepted!"); + + Thread worker = new Thread(new ParameterizedThreadStart(HandleClient!)); + worker.Start(); + } + finally + { + _tcpListener.Stop(); + } + } + } + + private void HandleClient(object clientObj) + { + TcpClient tcpClient = (TcpClient)clientObj; + + try + { + using NetworkStream stream = tcpClient.GetStream(); + + byte[] data = new byte[tcpClient.ReceiveBufferSize]; + while (stream.Read(data, 0, data.Length) != 0) + { + // Use the in-memory buffer to process the message + _messageHandler.HandleMessageAsync(stream.Socket, new MessageMemoryStream(data)).Wait(); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + + private async void Stop() + { + Console.WriteLine($"Stopping ${nameof(TcpGameServer)} listener..."); + _tcpListener.Stop(); + Console.WriteLine($"Stopped ${nameof(TcpGameServer)} listener."); + } + + public void Dispose() + { + Stop(); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/GServer.Server/TCPMessageHandler.cs b/GServer.Server/TcpMessageHandler.cs similarity index 73% rename from GServer.Server/TCPMessageHandler.cs rename to GServer.Server/TcpMessageHandler.cs index 30886be..6555972 100644 --- a/GServer.Server/TCPMessageHandler.cs +++ b/GServer.Server/TcpMessageHandler.cs @@ -1,5 +1,5 @@ using System.Net.Sockets; -using GServer.Common; +using GServer.Common.Networking.Core; using GServer.Common.Networking.Enums; using GServer.Common.Networking.Messages; using GServer.Common.Networking.Messages.Client; @@ -7,12 +7,8 @@ using GServer.Common.Networking.Messages.Server; namespace GServer.Server; -public class TCPMessageHandler : IMessageHandler +public class TcpMessageHandler : IMessageHandler { - public TCPMessageHandler() - { - } - public async Task HandleMessageAsync(Socket clientSocket, MessageMemoryStream messageStream) { ServerPacketIn serverPacketIn = (ServerPacketIn)messageStream.ReadByte(); @@ -21,11 +17,11 @@ public class TCPMessageHandler : IMessageHandler switch (serverPacketIn) { - case ServerPacketIn.AUTH: + case ServerPacketIn.Auth: AuthMessage msg = new(messageStream); - AuthResponseMessage resp = msg.Username == "aaronyarbz" && msg.Password == "password123" - ? new(true, Guid.NewGuid().ToString(), null) + AuthResponseMessage resp = msg is { Username: "aaronyarbz", Password: "password123" } + ? new(true, Guid.NewGuid().ToString(), failureReason: null) : new(false, null, AuthResponseFailure.IncorrectLoginOrPassword); byte[] buffer = resp.Serialize(); @@ -33,7 +29,7 @@ public class TCPMessageHandler : IMessageHandler break; - case ServerPacketIn.LIST_SERVERS: + case ServerPacketIn.ListServers: throw new NotImplementedException(); default: diff --git a/GServer.Server/UDPGameServer.cs b/GServer.Server/UDPGameServer.cs deleted file mode 100644 index a62e680..0000000 --- a/GServer.Server/UDPGameServer.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Net; -using System.Net.Sockets; -using GServer.Common; -using GServer.Common.Networking.Enums; - -namespace GServer.Server; - -public class UDPGameServer : IDisposable -{ - public readonly UdpClient UdpClient; - - private readonly IPEndPoint _endPoint; - - public UDPGameServer(IPEndPoint endPoint) - { - _endPoint = endPoint; - - UdpClient = new UdpClient(); - UdpClient.Client.SetSocketOption( - SocketOptionLevel.Socket, - SocketOptionName.ReuseAddress, - true); - } - - /// - /// Bind the server to the given endpoint. - /// - public void Start() - { - UdpClient.Client.Bind(_endPoint); - Console.WriteLine($"UDPGameServer listening on {_endPoint}"); - } - - /// - /// Begin processing messages - /// - /// - public async Task ProcessAsync() - { - try - { - UdpReceiveResult res = await UdpClient.ReceiveAsync(); - byte[] bytes = res.Buffer; - MessageMemoryStream stream = new(bytes); - await HandleMessageAsync(stream, res.RemoteEndPoint); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - private async Task HandleMessageAsync(MessageMemoryStream stream, IPEndPoint remoteEndPoint) - { - byte serverPacketInByte = (byte)stream.ReadByte(); - ServerPacketIn serverPacketIn = (ServerPacketIn)serverPacketInByte; - - Console.WriteLine($"Handling UDP message {serverPacketInByte} from {remoteEndPoint}..."); - - throw serverPacketIn switch - { - ServerPacketIn.AUTH => new NotImplementedException(), - ServerPacketIn.LIST_SERVERS => new NotImplementedException(), - _ => new NotImplementedException($"Received unsupported packet {serverPacketInByte}"), - }; - } - - public void Dispose() - { - UdpClient.Close(); - UdpClient.Dispose(); - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/GServer.sln b/GServer.sln index e661656..59bf784 100644 --- a/GServer.sln +++ b/GServer.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GServer.Server", "GServer.S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GServer.Client", "GServer.Client\GServer.Client.csproj", "{C105363D-E719-4296-94A2-01170E603889}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GServer.NCSServer", "GServer.NCSServer\GServer.NCSServer.csproj", "{29CBF617-0334-4DC0-A61E-6F03796AF66F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {C105363D-E719-4296-94A2-01170E603889}.Debug|Any CPU.Build.0 = Debug|Any CPU {C105363D-E719-4296-94A2-01170E603889}.Release|Any CPU.ActiveCfg = Release|Any CPU {C105363D-E719-4296-94A2-01170E603889}.Release|Any CPU.Build.0 = Release|Any CPU + {29CBF617-0334-4DC0-A61E-6F03796AF66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29CBF617-0334-4DC0-A61E-6F03796AF66F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29CBF617-0334-4DC0-A61E-6F03796AF66F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29CBF617-0334-4DC0-A61E-6F03796AF66F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE