feat: wip: implement TCPGameServer
This commit is contained in:
parent
35755eb7ec
commit
c640988354
11 changed files with 130 additions and 121 deletions
|
@ -13,9 +13,9 @@ internal class Program
|
||||||
{
|
{
|
||||||
IPEndPoint serverEP = new(IPAddress.Any, SERVER_PORT);
|
IPEndPoint serverEP = new(IPAddress.Any, SERVER_PORT);
|
||||||
|
|
||||||
UdpClient udpClient = new();
|
TcpClient tcpClient = new();
|
||||||
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||||
udpClient.Connect(serverEP);
|
tcpClient.Connect(serverEP);
|
||||||
|
|
||||||
Console.WriteLine("Username...");
|
Console.WriteLine("Username...");
|
||||||
string username = Console.ReadLine()!;
|
string username = Console.ReadLine()!;
|
||||||
|
@ -24,31 +24,35 @@ internal class Program
|
||||||
string password = Console.ReadLine()!;
|
string password = Console.ReadLine()!;
|
||||||
|
|
||||||
AuthMessage authMessage = new(username, password);
|
AuthMessage authMessage = new(username, password);
|
||||||
udpClient.Send(authMessage.Serialize());
|
_ = tcpClient.Client.Send(authMessage.Serialize());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
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();
|
ClientPacketIn packetIn = (ClientPacketIn)stream.ReadByte();
|
||||||
switch (packetIn)
|
switch (packetIn)
|
||||||
{
|
{
|
||||||
case ClientPacketIn.AUTH_RESPONSE:
|
case ClientPacketIn.AUTH_RESPONSE:
|
||||||
var authResultMessage = new AuthResponseMessage(stream);
|
AuthResponseMessage authResultMessage = new(stream);
|
||||||
|
|
||||||
Console.WriteLine("Success = " + authResultMessage.IsSuccessful);
|
Console.WriteLine("Success = " + authResultMessage.IsSuccessful);
|
||||||
|
|
||||||
Console.WriteLine("SessionToken = " + authResultMessage.SessionToken ?? "null");
|
Console.WriteLine("SessionToken = " + authResultMessage.SessionToken ?? "null");
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("FailureReason = " + authResultMessage.FailureReason ?? "null");
|
Console.WriteLine("FailureReason = " + authResultMessage.FailureReason ?? "null");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ClientPacketIn.LIST_SERVERS_RESPONSE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ClientPacketIn.UNKNOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"Received unsupported packet.");
|
Console.WriteLine($"Received unsupported packet.");
|
||||||
break;
|
break;
|
||||||
|
@ -61,7 +65,7 @@ internal class Program
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
udpClient.Close();
|
tcpClient.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ namespace GServer.Common;
|
||||||
|
|
||||||
public class MessageMemoryStream : MemoryStream
|
public class MessageMemoryStream : MemoryStream
|
||||||
{
|
{
|
||||||
|
public MessageMemoryStream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public MessageMemoryStream(byte[] buffer) : base(buffer)
|
public MessageMemoryStream(byte[] buffer) : base(buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class AuthResponseMessage : BaseMessage, IMessage<AuthResponseMessage>
|
||||||
FailureReason = failureReason;
|
FailureReason = failureReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthResponseMessage(MessageNetworkStream stream) : base((byte)ClientPacketIn.AUTH_RESPONSE)
|
public AuthResponseMessage(MessageMemoryStream stream) : base((byte)ClientPacketIn.AUTH_RESPONSE)
|
||||||
{
|
{
|
||||||
IsSuccessful = stream.ReadBoolean();
|
IsSuccessful = stream.ReadBoolean();
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class AuthResponseMessage : BaseMessage, IMessage<AuthResponseMessage>
|
||||||
|
|
||||||
public byte[] Serialize()
|
public byte[] Serialize()
|
||||||
{
|
{
|
||||||
using MessageNetworkStream stream = new();
|
using MessageMemoryStream stream = new();
|
||||||
|
|
||||||
stream.WriteByte(PacketId);
|
stream.WriteByte(PacketId);
|
||||||
stream.WriteBoolean(IsSuccessful);
|
stream.WriteBoolean(IsSuccessful);
|
||||||
|
|
|
@ -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<ListServersResponseMessage>
|
|
||||||
{
|
|
||||||
public IEnumerable<ServerListing> ServerListings { get; set; }
|
|
||||||
|
|
||||||
public ListServersResponseMessage(IEnumerable<ServerListing> 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();
|
|
||||||
}
|
|
||||||
}
|
|
8
GServer.Common/Networking/Messages/IMessageHandler.cs
Normal file
8
GServer.Common/Networking/Messages/IMessageHandler.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace GServer.Common.Networking.Messages;
|
||||||
|
|
||||||
|
public interface IMessageHandler
|
||||||
|
{
|
||||||
|
Task HandleMessageAsync(Socket clientSocket, MessageMemoryStream messageStream);
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ public class AuthMessage : BaseMessage, IMessage<AuthMessage>
|
||||||
Password = password;
|
Password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthMessage(MessageNetworkStream stream) : base((byte)ServerPacketIn.AUTH)
|
public AuthMessage(MessageMemoryStream stream) : base((byte)ServerPacketIn.AUTH)
|
||||||
{
|
{
|
||||||
byte usernameLen = (byte)stream.ReadByte();
|
byte usernameLen = (byte)stream.ReadByte();
|
||||||
string username = stream.ReadUTF8String(usernameLen);
|
string username = stream.ReadUTF8String(usernameLen);
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using GServer.Server;
|
using GServer.Server;
|
||||||
|
|
||||||
internal class Program
|
internal sealed class Program
|
||||||
{
|
{
|
||||||
private const int LISTEN_PORT = 11000;
|
private const int LISTEN_PORT = 11000;
|
||||||
|
|
||||||
|
|
||||||
private static void Main(string[] args)
|
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();
|
server.Start(cancellationTokenSource.Token).Wait();
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
_ = udpGameServer.ProcessAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,25 +1,24 @@
|
||||||
using GServer.Common.Game.Entities;
|
using GServer.Common.Game.Entities;
|
||||||
|
|
||||||
namespace GServer.Server.Services
|
namespace GServer.Server.Services;
|
||||||
{
|
|
||||||
public class ServerListService
|
|
||||||
{
|
|
||||||
public ServerListService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<ServerListing> List()
|
public class ServerListService
|
||||||
{
|
{
|
||||||
return [
|
public ServerListService()
|
||||||
new ServerListing
|
{
|
||||||
{
|
}
|
||||||
Name = "Smallville",
|
|
||||||
Description = "A tiny development server!",
|
public IEnumerable<ServerListing> List()
|
||||||
IPAddress = "localhost",
|
{
|
||||||
Port = 11001,
|
return [
|
||||||
Playercount = 1,
|
new ServerListing
|
||||||
}
|
{
|
||||||
];
|
Name = "Smallville",
|
||||||
}
|
Description = "A tiny development server!",
|
||||||
|
IPAddress = "localhost",
|
||||||
|
Port = 11001,
|
||||||
|
Playercount = 1,
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using GServer.Common;
|
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;
|
namespace GServer.Server;
|
||||||
|
|
||||||
|
@ -12,6 +10,7 @@ public class TCPGameServer : IDisposable
|
||||||
private readonly TcpListener _tcpListener;
|
private readonly TcpListener _tcpListener;
|
||||||
private readonly IPEndPoint _endPoint;
|
private readonly IPEndPoint _endPoint;
|
||||||
private readonly GameServerOptions _options;
|
private readonly GameServerOptions _options;
|
||||||
|
private readonly IMessageHandler _messageHandler;
|
||||||
|
|
||||||
public TCPGameServer(IPEndPoint endPoint, GameServerOptions options)
|
public TCPGameServer(IPEndPoint endPoint, GameServerOptions options)
|
||||||
{
|
{
|
||||||
|
@ -19,32 +18,42 @@ public class TCPGameServer : IDisposable
|
||||||
_options = options;
|
_options = options;
|
||||||
|
|
||||||
_tcpListener = new TcpListener(endPoint);
|
_tcpListener = new TcpListener(endPoint);
|
||||||
|
_messageHandler = new TCPMessageHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bind the server to the given endpoint.
|
/// Bind the server to the given endpoint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async void Start()
|
public async Task Start(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Starting TCPGameServer listener...");
|
Console.WriteLine("Starting TCPGameServer listener...");
|
||||||
_tcpListener.Start();
|
_tcpListener.Start();
|
||||||
|
_ = cancellationToken.Register(_tcpListener.Stop);
|
||||||
Console.WriteLine($"TCPGameServer listening on {_endPoint}");
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -57,7 +66,7 @@ public class TCPGameServer : IDisposable
|
||||||
{
|
{
|
||||||
using NetworkStream stream = tcpClient.GetStream();
|
using NetworkStream stream = tcpClient.GetStream();
|
||||||
|
|
||||||
byte[] data = new byte[_options.PacketLength];
|
byte[] data = new byte[tcpClient.ReceiveBufferSize];
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
int chunkSize = 1;
|
int chunkSize = 1;
|
||||||
|
|
||||||
|
@ -70,7 +79,7 @@ public class TCPGameServer : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the in-memory buffer to process the message
|
// 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)
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
TcpClient.Close();
|
|
||||||
TcpClient.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
44
GServer.Server/TCPMessageHandler.cs
Normal file
44
GServer.Server/TCPMessageHandler.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ public class UDPGameServer : IDisposable
|
||||||
{
|
{
|
||||||
UdpReceiveResult res = await UdpClient.ReceiveAsync();
|
UdpReceiveResult res = await UdpClient.ReceiveAsync();
|
||||||
byte[] bytes = res.Buffer;
|
byte[] bytes = res.Buffer;
|
||||||
MessageNetworkStream stream = new(bytes);
|
MessageMemoryStream stream = new(bytes);
|
||||||
await HandleMessageAsync(stream, res.RemoteEndPoint);
|
await HandleMessageAsync(stream, res.RemoteEndPoint);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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();
|
byte serverPacketInByte = (byte)stream.ReadByte();
|
||||||
ServerPacketIn serverPacketIn = (ServerPacketIn)serverPacketInByte;
|
ServerPacketIn serverPacketIn = (ServerPacketIn)serverPacketInByte;
|
||||||
|
|
||||||
Console.WriteLine($"Handling UDP message {serverPacketInByte} from {remoteEndPoint}...");
|
Console.WriteLine($"Handling UDP message {serverPacketInByte} from {remoteEndPoint}...");
|
||||||
|
|
||||||
switch (serverPacketIn)
|
throw serverPacketIn switch
|
||||||
{
|
{
|
||||||
default:
|
ServerPacketIn.AUTH => new NotImplementedException(),
|
||||||
throw new NotImplementedException($"Received unsupported packet {serverPacketInByte}");
|
ServerPacketIn.LIST_SERVERS => new NotImplementedException(),
|
||||||
}
|
_ => new NotImplementedException($"Received unsupported packet {serverPacketInByte}"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
Loading…
Add table
Reference in a new issue