diff --git a/GServer.Client/Program.cs b/GServer.Client/Program.cs index 60a6042..5b5605c 100644 --- a/GServer.Client/Program.cs +++ b/GServer.Client/Program.cs @@ -13,9 +13,9 @@ internal class Program { IPEndPoint serverEP = new(IPAddress.Any, SERVER_PORT); - UdpClient udpClient = new(); - udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - udpClient.Connect(serverEP); + TcpClient tcpClient = new(); + tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + tcpClient.Connect(serverEP); Console.WriteLine("Username..."); string username = Console.ReadLine()!; @@ -24,31 +24,35 @@ internal class Program string password = Console.ReadLine()!; AuthMessage authMessage = new(username, password); - udpClient.Send(authMessage.Serialize()); + _ = tcpClient.Client.Send(authMessage.Serialize()); try { while (true) { - byte[] bytes = udpClient.Receive(ref serverEP); + byte[] bytes = new byte[tcpClient.Client.ReceiveBufferSize]; + _ = tcpClient.Client.Receive(bytes); - MessageNetworkStream stream = new(bytes); + MessageMemoryStream stream = new(bytes); ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte(); switch (packetIn) { case ClientPacketIn.AUTH_RESPONSE: - var authResultMessage = new AuthResponseMessage(stream); + 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; @@ -61,7 +65,7 @@ internal class Program } finally { - udpClient.Close(); + tcpClient.Close(); } diff --git a/GServer.Common/Networking/Core/MessageMemoryStream.cs b/GServer.Common/Networking/Core/MessageMemoryStream.cs index 3e0db44..b2ea9b8 100644 --- a/GServer.Common/Networking/Core/MessageMemoryStream.cs +++ b/GServer.Common/Networking/Core/MessageMemoryStream.cs @@ -4,6 +4,10 @@ namespace GServer.Common; public class MessageMemoryStream : MemoryStream { + public MessageMemoryStream() + { + } + public MessageMemoryStream(byte[] buffer) : base(buffer) { } diff --git a/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs b/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs index 8315f26..ad69d4d 100644 --- a/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs +++ b/GServer.Common/Networking/Messages/Client/AuthResponseMessage.cs @@ -30,7 +30,7 @@ public class AuthResponseMessage : BaseMessage, IMessage FailureReason = failureReason; } - public AuthResponseMessage(MessageNetworkStream stream) : base((byte)ClientPacketIn.AUTH_RESPONSE) + public AuthResponseMessage(MessageMemoryStream stream) : base((byte)ClientPacketIn.AUTH_RESPONSE) { IsSuccessful = stream.ReadBoolean(); @@ -47,7 +47,7 @@ public class AuthResponseMessage : BaseMessage, IMessage public byte[] Serialize() { - using MessageNetworkStream stream = new(); + using MessageMemoryStream stream = new(); stream.WriteByte(PacketId); stream.WriteBoolean(IsSuccessful); diff --git a/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs b/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs deleted file mode 100644 index ce3c34b..0000000 --- a/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GServer.Common.Game.Entities; -using GServer.Common.Networking.Enums; - -namespace GServer.Common.Networking.Messages.Client; - -public class ListServersResponseMessage : BaseMessage, IMessage -{ - public IEnumerable ServerListings { get; set; } - - public ListServersResponseMessage(IEnumerable serverListings) : base((byte)ClientPacketIn.LIST_SERVERS_RESPONSE) - { - ServerListings = serverListings; - } - - public ListServersResponseMessage(MessageNetworkStream stream) : base((byte)ClientPacketIn.LIST_SERVERS_RESPONSE) - { - throw new NotImplementedException(); - } - - public byte[] Serialize() - { - throw new NotImplementedException(); - } -} diff --git a/GServer.Common/Networking/Messages/IMessageHandler.cs b/GServer.Common/Networking/Messages/IMessageHandler.cs new file mode 100644 index 0000000..42e3866 --- /dev/null +++ b/GServer.Common/Networking/Messages/IMessageHandler.cs @@ -0,0 +1,8 @@ +using System.Net.Sockets; + +namespace GServer.Common.Networking.Messages; + +public interface IMessageHandler +{ + Task HandleMessageAsync(Socket clientSocket, MessageMemoryStream messageStream); +} \ No newline at end of file diff --git a/GServer.Common/Networking/Messages/Server/AuthMessage.cs b/GServer.Common/Networking/Messages/Server/AuthMessage.cs index ad797ff..0ad0750 100644 --- a/GServer.Common/Networking/Messages/Server/AuthMessage.cs +++ b/GServer.Common/Networking/Messages/Server/AuthMessage.cs @@ -14,7 +14,7 @@ public class AuthMessage : BaseMessage, IMessage Password = password; } - public AuthMessage(MessageNetworkStream stream) : base((byte)ServerPacketIn.AUTH) + public AuthMessage(MessageMemoryStream stream) : base((byte)ServerPacketIn.AUTH) { byte usernameLen = (byte)stream.ReadByte(); string username = stream.ReadUTF8String(usernameLen); diff --git a/GServer.Server/Program.cs b/GServer.Server/Program.cs index bb03612..bbaadc9 100644 --- a/GServer.Server/Program.cs +++ b/GServer.Server/Program.cs @@ -1,20 +1,15 @@ using System.Net; using GServer.Server; -internal class Program +internal sealed class Program { private const int LISTEN_PORT = 11000; - private static void Main(string[] args) { - UDPGameServer udpGameServer = new(new IPEndPoint(IPAddress.Any, LISTEN_PORT)); + TCPGameServer server = new(new IPEndPoint(IPAddress.Any, LISTEN_PORT), new GameServerOptions()); + CancellationTokenSource cancellationTokenSource = new(); - udpGameServer.Start(); - - while (true) - { - _ = udpGameServer.ProcessAsync(); - } + server.Start(cancellationTokenSource.Token).Wait(); } } \ No newline at end of file diff --git a/GServer.Server/Services/ServerListService.cs b/GServer.Server/Services/ServerListService.cs index 4c7fbc0..f11b38d 100644 --- a/GServer.Server/Services/ServerListService.cs +++ b/GServer.Server/Services/ServerListService.cs @@ -1,25 +1,24 @@ using GServer.Common.Game.Entities; -namespace GServer.Server.Services -{ - public class ServerListService - { - public ServerListService() - { - } +namespace GServer.Server.Services; - public IEnumerable List() - { - return [ - new ServerListing - { - Name = "Smallville", - Description = "A tiny development server!", - IPAddress = "localhost", - Port = 11001, - Playercount = 1, - } - ]; - } +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 index ffe46db..3e49f81 100644 --- a/GServer.Server/TCPGameServer.cs +++ b/GServer.Server/TCPGameServer.cs @@ -1,9 +1,7 @@ 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; +using GServer.Common.Networking.Messages; namespace GServer.Server; @@ -12,6 +10,7 @@ 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) { @@ -19,32 +18,42 @@ public class TCPGameServer : IDisposable _options = options; _tcpListener = new TcpListener(endPoint); + _messageHandler = new TCPMessageHandler(); } /// /// Bind the server to the given endpoint. /// - public async void Start() + public async Task Start(CancellationToken cancellationToken) { Console.WriteLine("Starting TCPGameServer listener..."); _tcpListener.Start(); + _ = cancellationToken.Register(_tcpListener.Stop); Console.WriteLine($"TCPGameServer listening on {_endPoint}"); - try + while (!cancellationToken.IsCancellationRequested) { - while (true) + try { - await ProcessAsync(await _tcpListener.AcceptTcpClientAsync()); + 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(); } } - catch - { - throw; - } - finally - { - _tcpListener.Stop(); - } + } /// @@ -57,7 +66,7 @@ public class TCPGameServer : IDisposable { using NetworkStream stream = tcpClient.GetStream(); - byte[] data = new byte[_options.PacketLength]; + byte[] data = new byte[tcpClient.ReceiveBufferSize]; int bytesRead = 0; int chunkSize = 1; @@ -70,7 +79,7 @@ public class TCPGameServer : IDisposable } // Use the in-memory buffer to process the message - await HandleMessageAsync(stream.Socket, new MessageMemoryStream(data)); + await _messageHandler.HandleMessageAsync(stream.Socket, new MessageMemoryStream(data)); } catch (Exception ex) { @@ -78,39 +87,8 @@ public class TCPGameServer : IDisposable } } - private async Task HandleMessageAsync(Socket clientSocket, MessageMemoryStream messageStream) - { - ServerPacketIn serverPacketIn = (ServerPacketIn)messageStream.ReadByte(); - - Console.WriteLine($"Handling message {serverPacketIn} from {client}..."); - - switch (serverPacketIn) - { - case ServerPacketIn.AUTH: - AuthMessage msg = new(messageStream); - - AuthResponseMessage resp = msg.Username == "aaronyarbz" && msg.Password == "password123" - ? new(true, Guid.NewGuid().ToString(), null) - : new(false, null, AuthResponseFailure.IncorrectLoginOrPassword); - - byte[] buffer = resp.Serialize(); - _ = await TcpClient.Client.SendAsync(buffer, - - break; - - case ServerPacketIn.LIST_SERVERS: - throw new NotImplementedException(); - - default: - Console.WriteLine($"Received unsupported packet."); - break; - } - } - public void Dispose() { - TcpClient.Close(); - TcpClient.Dispose(); GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/GServer.Server/TCPMessageHandler.cs b/GServer.Server/TCPMessageHandler.cs new file mode 100644 index 0000000..30886be --- /dev/null +++ b/GServer.Server/TCPMessageHandler.cs @@ -0,0 +1,44 @@ +using System.Net.Sockets; +using GServer.Common; +using GServer.Common.Networking.Enums; +using GServer.Common.Networking.Messages; +using GServer.Common.Networking.Messages.Client; +using GServer.Common.Networking.Messages.Server; + +namespace GServer.Server; + +public class TCPMessageHandler : IMessageHandler +{ + public TCPMessageHandler() + { + } + + public async Task HandleMessageAsync(Socket clientSocket, MessageMemoryStream messageStream) + { + ServerPacketIn serverPacketIn = (ServerPacketIn)messageStream.ReadByte(); + + Console.WriteLine($"Handling message {serverPacketIn} from {clientSocket.RemoteEndPoint}..."); + + switch (serverPacketIn) + { + case ServerPacketIn.AUTH: + AuthMessage msg = new(messageStream); + + AuthResponseMessage resp = msg.Username == "aaronyarbz" && msg.Password == "password123" + ? new(true, Guid.NewGuid().ToString(), null) + : new(false, null, AuthResponseFailure.IncorrectLoginOrPassword); + + byte[] buffer = resp.Serialize(); + _ = await clientSocket.SendAsync(buffer); + + break; + + case ServerPacketIn.LIST_SERVERS: + throw new NotImplementedException(); + + default: + Console.WriteLine($"Received unsupported packet."); + break; + } + } +} \ No newline at end of file diff --git a/GServer.Server/UDPGameServer.cs b/GServer.Server/UDPGameServer.cs index b00bcbe..a62e680 100644 --- a/GServer.Server/UDPGameServer.cs +++ b/GServer.Server/UDPGameServer.cs @@ -41,7 +41,7 @@ public class UDPGameServer : IDisposable { UdpReceiveResult res = await UdpClient.ReceiveAsync(); byte[] bytes = res.Buffer; - MessageNetworkStream stream = new(bytes); + MessageMemoryStream stream = new(bytes); await HandleMessageAsync(stream, res.RemoteEndPoint); } catch (Exception ex) @@ -50,18 +50,19 @@ public class UDPGameServer : IDisposable } } - private async Task HandleMessageAsync(MessageNetworkStream stream, IPEndPoint remoteEndPoint) + 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}..."); - switch (serverPacketIn) + throw serverPacketIn switch { - default: - throw new NotImplementedException($"Received unsupported packet {serverPacketInByte}"); - } + ServerPacketIn.AUTH => new NotImplementedException(), + ServerPacketIn.LIST_SERVERS => new NotImplementedException(), + _ => new NotImplementedException($"Received unsupported packet {serverPacketInByte}"), + }; } public void Dispose()