diff --git a/GServer.Client/Program.cs b/GServer.Client/Program.cs index fbc0626..471cb98 100644 --- a/GServer.Client/Program.cs +++ b/GServer.Client/Program.cs @@ -1,5 +1,6 @@ using System.Net; using System.Net.Sockets; +using GServer.Common.Game.Entities; using GServer.Common.Networking.Core; using GServer.Common.Networking.Enums; using GServer.Common.Networking.Messages.Client; @@ -7,10 +8,12 @@ using GServer.Common.Networking.Messages.Server; namespace GServer.Client; -public class Program +public static class Program { private const int ServerPort = 11000; + private static string? _sessionToken; + private static void Main(string[] args) { IPEndPoint serverEp = new(IPAddress.Any, ServerPort); @@ -18,46 +21,70 @@ public class Program 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) + while (_sessionToken is null) { - byte[] bytes = new byte[tcpClient.Client.ReceiveBufferSize]; - _ = tcpClient.Client.Receive(bytes); + Console.WriteLine("Username..."); + string username = Console.ReadLine()!; - MessageMemoryStream stream = new(bytes); + Console.WriteLine("Password..."); + string password = Console.ReadLine()!; - ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte(); - switch (packetIn) + AuthMessage authMessage = new(username, password); + _ = tcpClient.Client.Send(authMessage.Serialize()); + + while (true) { - case ClientPacketIn.AuthResponse: - AuthResponseMessage authResultMessage = new(stream); + Console.WriteLine("Listening for message..."); + + byte[] bytes = new byte[tcpClient.Client.ReceiveBufferSize]; + _ = tcpClient.Client.Receive(bytes); - Console.WriteLine("Success = " + authResultMessage.IsSuccessful); - Console.WriteLine("SessionToken = " + authResultMessage.SessionToken); - Console.WriteLine("FailureReason = " + authResultMessage.FailureReason); + MessageMemoryStream stream = new(bytes); - break; + ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte(); + switch (packetIn) + { + case ClientPacketIn.AuthResponse: + AuthResponseMessage authResultMessage = new(stream); - case ClientPacketIn.ListServersResponse: - break; + Console.WriteLine("Success = " + authResultMessage.IsSuccessful); + Console.WriteLine("SessionToken = " + authResultMessage.SessionToken); + Console.WriteLine("FailureReason = " + authResultMessage.FailureReason); - case ClientPacketIn.Unknown: - break; + // Request server list as soon as login has succeeded - default: - Console.WriteLine("Received unsupported packet."); - break; + if (authResultMessage.IsSuccessful) + { + Console.WriteLine("Getting server list..."); + _ = tcpClient.Client.Send(new[] { (byte)ServerPacketIn.ListServers }); + } + + break; + + case ClientPacketIn.ListServersResponse: + ListServersResponseMessage listServersResponseMessage = new(stream); + + Console.WriteLine("Here are the servers!"); + + foreach (ServerListing server in listServersResponseMessage.ServerListings) + { + Console.WriteLine(server.Name); + Console.WriteLine($"Desc: {server.Description}"); + Console.WriteLine($"Address: {server.IpAddress}:{server.Port}"); + Console.WriteLine($"Tier: {server.ServerTier}"); + } + break; + + case ClientPacketIn.Unknown: + break; + + default: + Console.WriteLine("Received unsupported packet."); + break; + } } } } @@ -70,4 +97,5 @@ public class Program tcpClient.Close(); } } -} \ No newline at end of file +} + diff --git a/GServer.Common/Networking/Core/MessageMemoryStream.cs b/GServer.Common/Networking/Core/MessageMemoryStream.cs index 28cdda6..ee8e7d6 100644 --- a/GServer.Common/Networking/Core/MessageMemoryStream.cs +++ b/GServer.Common/Networking/Core/MessageMemoryStream.cs @@ -31,6 +31,13 @@ public class MessageMemoryStream : MemoryStream return BitConverter.ToInt16(buffer); } + public long ReadInt64() + { + byte[] buffer = new byte[8]; + _ = Read(buffer, 0, 8); + return BitConverter.ToInt64(buffer); + } + public string ReadUtf8String(int length) { byte[] bytes = new byte[length]; diff --git a/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs b/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs new file mode 100644 index 0000000..accbc95 --- /dev/null +++ b/GServer.Common/Networking/Messages/Client/ListServersResponseMessage.cs @@ -0,0 +1,69 @@ +using GServer.Common.Game.Entities; +using GServer.Common.Networking.Core; +using GServer.Common.Networking.Enums; + +namespace GServer.Common.Networking.Messages.Server; + +public class ListServersResponseMessage : BaseMessage, IMessage +{ + public ServerListing[] ServerListings; + + public const int SERVER_NAME_BYTES = 50; + public const int SERVER_DESCRIPTION_BYTES = 1000; + public const int SERVER_IP_BYTES = 15; + + public ListServersResponseMessage(ServerListing[] serverListings) : base((byte)ClientPacketIn.ListServersResponse) + { + ServerListings = serverListings; + } + + public ListServersResponseMessage(MessageMemoryStream stream) : base((byte)ClientPacketIn.ListServersResponse) + { + List serverListings = []; + + while (stream.Position != stream.Length) + { + // TODO: remove this from the packet as I don't think it's needed + // long blockSize = stream.ReadInt64(); + + ServerListing serverListing = new() + { + Name = stream.ReadUtf8String(SERVER_NAME_BYTES), + Description = stream.ReadUtf8String(SERVER_DESCRIPTION_BYTES), + Playercount = stream.ReadUInt16(), + IpAddress = stream.ReadUtf8String(SERVER_IP_BYTES), + Port = stream.ReadUInt16(), + ServerTier = (ServerTier)stream.ReadByte() + }; + + serverListings.Add(serverListing); + } + + ServerListings = serverListings.ToArray(); + } + + public byte[] Serialize() + { + using MessageMemoryStream stream = new(); + + stream.WriteByte(PacketId); + + foreach (ServerListing listing in ServerListings) + { + using MessageMemoryStream listingStream = new(); + + listingStream.WriteUtf8StringOfSize(SERVER_NAME_BYTES, listing.Name); + listingStream.WriteUtf8StringOfSize(SERVER_DESCRIPTION_BYTES, listing.Description); + listingStream.WriteUInt16(listing.Playercount); + listingStream.WriteUtf8StringOfSize(15, listing.IpAddress); + listingStream.WriteUInt16(listing.Port); + listingStream.WriteByte((byte)listing.ServerTier); + + long listingBlockSize = listingStream.Length; + stream.WriteInt64(listingBlockSize); + stream.WriteBytes(listingStream.ToArray()); + } + + return stream.ToArray(); + } +} diff --git a/GServer.Common/Networking/Messages/Server/ListServersMessage.cs b/GServer.Common/Networking/Messages/Server/ListServersMessage.cs deleted file mode 100644 index 7c17c02..0000000 --- a/GServer.Common/Networking/Messages/Server/ListServersMessage.cs +++ /dev/null @@ -1,42 +0,0 @@ -using GServer.Common.Game.Entities; -using GServer.Common.Networking.Core; -using GServer.Common.Networking.Enums; - -namespace GServer.Common.Networking.Messages.Server; - -public class ListServersMessage : BaseMessage, IMessage -{ - public ServerListing[] ServerListings; - - public const int SERVER_NAME_BYTES = 50; - - public ListServersMessage(ServerListing[] serverListings) : base((byte)ServerPacketIn.ListServers) - { - ServerListings = serverListings; - } - - public byte[] Serialize() - { - using MessageMemoryStream stream = new(); - - stream.WriteByte(PacketId); - - foreach (ServerListing listing in ServerListings) - { - using MessageMemoryStream listingStream = new(); - - listingStream.WriteUtf8StringOfSize(50, listing.Name); - listingStream.WriteUtf8StringOfSize(1000, listing.Description); - listingStream.WriteUInt16(listing.Playercount); - listingStream.WriteUtf8StringOfSize(15, listing.IpAddress); - listingStream.WriteUInt16(listing.Port); - listingStream.WriteByte((byte)listing.ServerTier); - - long listingBlockSize = listingStream.Length; - stream.WriteInt64(listingBlockSize); - stream.WriteBytes(listingStream.ToArray()); - } - - return stream.ToArray(); - } -} diff --git a/GServer.Server/TcpMessageHandler.cs b/GServer.Server/TcpMessageHandler.cs index 306db1d..fd8cbd3 100644 --- a/GServer.Server/TcpMessageHandler.cs +++ b/GServer.Server/TcpMessageHandler.cs @@ -1,4 +1,6 @@ using System.Net.Sockets; +using GServer.Common; +using GServer.Common.Game.Entities; using GServer.Common.Networking.Core; using GServer.Common.Networking.Enums; using GServer.Common.Networking.Messages; @@ -26,24 +28,51 @@ public class TcpMessageHandler( switch (serverPacketIn) { case ServerPacketIn.Auth: + { AuthMessage msg = new(messageStream); bool isPasswordCorrect = authService.IsPasswordCorrect(msg.Username, msg.Password); AuthResponseMessage resp = isPasswordCorrect - ? new(true, Guid.NewGuid().ToString(), failureReason: null) - : new(false, null, AuthResponseFailure.IncorrectLoginOrPassword); - - byte[] buffer = resp.Serialize(); - _ = await clientSocket.SendAsync(buffer); + ? new AuthResponseMessage(true, Guid.NewGuid().ToString(), failureReason: null) + : new AuthResponseMessage(false, null, AuthResponseFailure.IncorrectLoginOrPassword); + await SendMessageAsync(resp, clientSocket); break; + } case ServerPacketIn.ListServers: - throw new NotImplementedException(); + { + ServerListing[] serverListings = + [ + new() + { + Name = "Testbed", + Description = "A server to hone your development skills and test new ideas.", + IpAddress = "255.255.255.255", + Port = 1337, + Playercount = 12, + ServerTier = ServerTier.Default + } + ]; + + ListServersResponseMessage resp = new(serverListings); + await SendMessageAsync(resp, clientSocket); + + break; + } default: Console.WriteLine($"Received unsupported packet."); break; } } -} \ No newline at end of file + + private static async Task SendMessageAsync( + TMessage message, + Socket clientSocket) where TMessage : IMessage + { + byte[] buffer = message.Serialize(); + _ = await clientSocket.SendAsync(buffer); + } +} +