diff --git a/Components/MineSharp.Protocol/MinecraftClient.cs b/Components/MineSharp.Protocol/MinecraftClient.cs
index 16bad91d..ca8fda3e 100644
--- a/Components/MineSharp.Protocol/MinecraftClient.cs
+++ b/Components/MineSharp.Protocol/MinecraftClient.cs
@@ -7,7 +7,6 @@
using MineSharp.Protocol.Exceptions;
using MineSharp.Protocol.Packets;
using MineSharp.Protocol.Packets.Clientbound.Status;
-using MineSharp.Protocol.Packets.Handlers;
using MineSharp.Protocol.Packets.Serverbound.Status;
using NLog;
using System.Diagnostics;
@@ -15,6 +14,9 @@
using System.Net;
using MineSharp.Protocol.Packets.Serverbound.Configuration;
using Newtonsoft.Json.Linq;
+using MineSharp.Protocol.Packets.Handlers;
+using ClientPacketHandlers = MineSharp.Protocol.Packets.Handlers.Client;
+using ServerPacketHandlers = MineSharp.Protocol.Packets.Handlers.Server;
namespace MineSharp.Protocol;
@@ -77,26 +79,30 @@ public sealed class MinecraftClient : IDisposable
///
public event Events.ClientStringEvent? OnDisconnected;
+ ///
+ ///
+ ///
+ public readonly bool isServerClient;
///
/// The MinecraftData object of this client
///
- public readonly MinecraftData Data;
+ public MinecraftData Data { get; private set; }
///
/// The Session object for this client
///
- public readonly Session Session;
+ public readonly Session? Session;
///
/// The Hostname of the minecraft server provided in the constructor
///
- public readonly string Hostname;
+ public string Hostname { get; private set; }
///
/// The Port of the minecraft server
///
- public readonly ushort Port;
+ public ushort Port { get; private set; }
///
/// The clients settings
@@ -118,7 +124,7 @@ public MinecraftClient(
this.Data = data;
this._packetQueue = new ConcurrentQueue();
this._cancellation = new CancellationTokenSource();
- this._internalPacketHandler = new HandshakePacketHandler(this);
+ this._internalPacketHandler = new ClientPacketHandlers.HandshakePacketHandler(this);
this._packetHandlers = new Dictionary>();
this._packetWaiters = new Dictionary>();
this._gameJoinedTsc = new TaskCompletionSource();
@@ -136,8 +142,46 @@ public MinecraftClient(
this.Hostname = hostnameOrIp;
this.gameState = GameState.Handshaking;
this.Settings = settings ?? ClientSettings.Default;
+
+ this.isServerClient = false;
}
+ ///
+ /// Constructor for MinecraftClient that is used for MinecraftServer
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal MinecraftClient(
+ TcpClient client,
+ IPAddress ipAddress,
+ MinecraftData data,
+ Session? session = null,
+ string? hostname = null,
+ ClientSettings? settings = null
+ )
+ {
+ this.ip = ipAddress;
+ this.Data = data;
+ this.Session = session;
+ this.Hostname = hostname ?? "";
+ this.Port = 25565;
+ this.Settings = settings ?? ClientSettings.Default;
+ this._client = client;
+
+ this._packetQueue = new ConcurrentQueue();
+ this._cancellation = new CancellationTokenSource();
+ this._internalPacketHandler = new ServerPacketHandlers.HandshakePacketHandler(this);
+ this._packetHandlers = new Dictionary>();
+ this._packetWaiters = new Dictionary>();
+ this._gameJoinedTsc = new TaskCompletionSource();
+ this._bundledPackets = new Queue<(PacketType, PacketBuffer)>();
+
+ this.isServerClient = true;
+ }
///
/// Connects to the minecraft sever.
///
@@ -168,8 +212,11 @@ public async Task Connect(GameState nextState)
this._stream = new MinecraftStream(this._client.GetStream(), this._useAnonymousNbt);
this.StreamLoop();
- Logger.Info("Connected, starting handshake...");
- await HandshakeProtocol.PerformHandshake(this, nextState, this.Data);
+ if (!isServerClient)
+ {
+ Logger.Info("Connected, starting handshake...");
+ await HandshakeProtocol.PerformHandshake(this, nextState, this.Data);
+ }
}
catch (SocketException ex)
{
@@ -265,30 +312,48 @@ internal void UpdateGameState(GameState next)
{
this.gameState = next;
- this._internalPacketHandler = next switch
+ if (!this.isServerClient)
{
- GameState.Handshaking => new HandshakePacketHandler(this),
- GameState.Login => new LoginPacketHandler(this, this.Data),
- GameState.Status => new StatusPacketHandler(this),
- GameState.Configuration => new ConfigurationPacketHandler(this, this.Data),
- GameState.Play => new PlayPacketHandler(this, this.Data),
- _ => throw new UnreachableException()
- };
-
- if (next == GameState.Play)
- this._gameJoinedTsc.TrySetResult();
-
- if (next == GameState.Configuration)
+ this._internalPacketHandler = next switch
+ {
+ GameState.Handshaking => new ClientPacketHandlers.HandshakePacketHandler(this),
+ GameState.Login => new ClientPacketHandlers.LoginPacketHandler(this, this.Data),
+ GameState.Status => new ClientPacketHandlers.StatusPacketHandler(this),
+ GameState.Configuration => new ClientPacketHandlers.ConfigurationPacketHandler(this, this.Data),
+ GameState.Play => new ClientPacketHandlers.PlayPacketHandler(this, this.Data),
+ _ => throw new UnreachableException()
+ };
+
+ if (next == GameState.Play)
+ this._gameJoinedTsc.TrySetResult();
+
+ if (next == GameState.Configuration)
+ {
+ this.SendPacket(new ClientInformationPacket(
+ this.Settings.Locale,
+ this.Settings.ViewDistance,
+ (int)this.Settings.ChatMode,
+ this.Settings.ColoredChat,
+ this.Settings.DisplayedSkinParts,
+ (int)this.Settings.MainHand,
+ this.Settings.EnableTextFiltering,
+ this.Settings.AllowServerListings));
+ }
+ } else
{
- this.SendPacket(new ClientInformationPacket(
- this.Settings.Locale,
- this.Settings.ViewDistance,
- (int)this.Settings.ChatMode,
- this.Settings.ColoredChat,
- this.Settings.DisplayedSkinParts,
- (int)this.Settings.MainHand,
- this.Settings.EnableTextFiltering,
- this.Settings.AllowServerListings));
+ this._internalPacketHandler = next switch
+ {
+ GameState.Handshaking => new ServerPacketHandlers.HandshakePacketHandler(this),
+ GameState.Login => new ServerPacketHandlers.LoginPacketHandler(this, this.Data),
+ GameState.Status => new ServerPacketHandlers.StatusPacketHandler(this),
+ GameState.Configuration => new ServerPacketHandlers.ConfigurationPacketHandler(this, this.Data),
+ GameState.Play => new ServerPacketHandlers.PlayPacketHandler(this, this.Data),
+ _ => throw new UnreachableException()
+ };
+
+ if (next == GameState.Play)
+ this._gameJoinedTsc.TrySetResult();
+
}
}
@@ -351,7 +416,7 @@ private async Task ReceivePackets()
{
var buffer = this._stream!.ReadPacket();
var packetId = buffer.ReadVarInt();
- var packetType = this.Data.Protocol.GetPacketType(PacketFlow.Clientbound, this.gameState, packetId);
+ var packetType = this.Data.Protocol.GetPacketType(isServerClient ? PacketFlow.Serverbound : PacketFlow.Clientbound, this.gameState, packetId);
if (this._bundlePackets)
this._bundledPackets.Enqueue((packetType, buffer));
@@ -480,6 +545,11 @@ private async Task HandleOutgoingPacket(IPacket packet)
});
}
+ internal void HandshakeUpdateHostAndPort(string host, ushort port)
+ {
+ Hostname = host;
+ Port = port;
+ }
private Task InvokeReceivePacketAsync(IPacket packet)
{
return Task.Run(() => this.OnPacketReceived?.Invoke(this, packet));
diff --git a/Components/MineSharp.Protocol/MinecraftServer.cs b/Components/MineSharp.Protocol/MinecraftServer.cs
new file mode 100644
index 00000000..7c9dbec6
--- /dev/null
+++ b/Components/MineSharp.Protocol/MinecraftServer.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+using MineSharp.Auth;
+using MineSharp.Core.Common.Protocol;
+using MineSharp.Data;
+using NLog;
+using Org.BouncyCastle.Crypto.Tls;
+
+namespace MineSharp.Protocol;
+
+///
+/// Minecraft server class
+///
+public sealed class MinecraftServer
+{
+ ///
+ /// The latest version supported
+ ///
+ public const string LATEST_SUPPORTED_VERSION = "1.20.4";
+
+ private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
+
+ private readonly MinecraftData Data;
+
+ private readonly IPAddress _address;
+ private readonly ushort _port;
+ private TcpListener? _listener;
+
+ internal readonly MinecraftApi? Api;
+
+
+ ///
+ /// The constructor for the MinecraftServer class
+ ///
+ public MinecraftServer(
+ MinecraftData data,
+ IPAddress address,
+ ushort port)
+ {
+ this.Data = data;
+ this._address = address;
+ this._port = port;
+ }
+
+ ///
+ /// Start the Minecraft server
+ ///
+ public void Start()
+ {
+ _listener = new TcpListener(_address, _port);
+
+ _listener.Start();
+
+ Task.Run(ListenForNewClients);
+ }
+
+ private void ListenForNewClients()
+ {
+ while (true)
+ {
+ using TcpClient client = _listener!.AcceptTcpClient();
+
+ Task.Run(() => HandleNewClient(client));
+ }
+ }
+
+ private void HandleNewClient(TcpClient client)
+ {
+ MinecraftClient minecraftClient = new MinecraftClient(client, _address, Data);
+ minecraftClient.UpdateGameState(GameState.Handshaking);
+ }
+}
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/ConfigurationPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Client/ConfigurationPacketHandler.cs
similarity index 96%
rename from Components/MineSharp.Protocol/Packets/Handlers/ConfigurationPacketHandler.cs
rename to Components/MineSharp.Protocol/Packets/Handlers/Client/ConfigurationPacketHandler.cs
index c609bcd8..1bf99702 100644
--- a/Components/MineSharp.Protocol/Packets/Handlers/ConfigurationPacketHandler.cs
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Client/ConfigurationPacketHandler.cs
@@ -1,10 +1,10 @@
-using MineSharp.Core.Common.Protocol;
+using MineSharp.Core.Common.Protocol;
using MineSharp.Data;
using MineSharp.Data.Protocol;
using MineSharp.Protocol.Packets.Clientbound.Configuration;
using NLog;
-namespace MineSharp.Protocol.Packets.Handlers;
+namespace MineSharp.Protocol.Packets.Handlers.Client;
internal class ConfigurationPacketHandler : IPacketHandler
{
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/HandshakePacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Client/HandshakePacketHandler.cs
similarity index 91%
rename from Components/MineSharp.Protocol/Packets/Handlers/HandshakePacketHandler.cs
rename to Components/MineSharp.Protocol/Packets/Handlers/Client/HandshakePacketHandler.cs
index fbb1c15a..e3354f0a 100644
--- a/Components/MineSharp.Protocol/Packets/Handlers/HandshakePacketHandler.cs
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Client/HandshakePacketHandler.cs
@@ -1,8 +1,8 @@
-using MineSharp.Data.Protocol;
+using MineSharp.Data.Protocol;
using MineSharp.Protocol.Exceptions;
using MineSharp.Protocol.Packets.Serverbound.Handshaking;
-namespace MineSharp.Protocol.Packets.Handlers;
+namespace MineSharp.Protocol.Packets.Handlers.Client;
internal class HandshakePacketHandler : IPacketHandler
{
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Client/LoginPacketHandler.cs
similarity index 98%
rename from Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs
rename to Components/MineSharp.Protocol/Packets/Handlers/Client/LoginPacketHandler.cs
index bad5bb89..807eaad8 100644
--- a/Components/MineSharp.Protocol/Packets/Handlers/LoginPacketHandler.cs
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Client/LoginPacketHandler.cs
@@ -1,4 +1,4 @@
-using MineSharp.Auth.Exceptions;
+using MineSharp.Auth.Exceptions;
using MineSharp.Core.Common;
using MineSharp.Core.Common.Protocol;
using MineSharp.Data;
@@ -10,7 +10,7 @@
using System.Diagnostics;
using System.Security.Cryptography;
-namespace MineSharp.Protocol.Packets.Handlers;
+namespace MineSharp.Protocol.Packets.Handlers.Client;
internal class LoginPacketHandler : IPacketHandler
{
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Client/PlayPacketHandler.cs
similarity index 97%
rename from Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs
rename to Components/MineSharp.Protocol/Packets/Handlers/Client/PlayPacketHandler.cs
index dc848be1..01428059 100644
--- a/Components/MineSharp.Protocol/Packets/Handlers/PlayPacketHandler.cs
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Client/PlayPacketHandler.cs
@@ -4,7 +4,7 @@
using MineSharp.Protocol.Packets.Serverbound.Play;
using KeepAlivePacket = MineSharp.Protocol.Packets.Clientbound.Play.KeepAlivePacket;
-namespace MineSharp.Protocol.Packets.Handlers;
+namespace MineSharp.Protocol.Packets.Handlers.Client;
internal class PlayPacketHandler : IPacketHandler
{
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/StatusPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Client/StatusPacketHandler.cs
similarity index 82%
rename from Components/MineSharp.Protocol/Packets/Handlers/StatusPacketHandler.cs
rename to Components/MineSharp.Protocol/Packets/Handlers/Client/StatusPacketHandler.cs
index 8b7000ab..cbf63c06 100644
--- a/Components/MineSharp.Protocol/Packets/Handlers/StatusPacketHandler.cs
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Client/StatusPacketHandler.cs
@@ -1,6 +1,6 @@
-using MineSharp.Data.Protocol;
+using MineSharp.Data.Protocol;
-namespace MineSharp.Protocol.Packets.Handlers;
+namespace MineSharp.Protocol.Packets.Handlers.Client;
internal class StatusPacketHandler : IPacketHandler
{
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/Server/ConfigurationPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Server/ConfigurationPacketHandler.cs
new file mode 100644
index 00000000..997c1c5e
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Server/ConfigurationPacketHandler.cs
@@ -0,0 +1,75 @@
+using MineSharp.Core.Common.Protocol;
+using MineSharp.Data;
+using MineSharp.Data.Protocol;
+using MineSharp.Protocol.Packets.Clientbound.Configuration;
+using NLog;
+
+namespace MineSharp.Protocol.Packets.Handlers.Server;
+
+internal class ConfigurationPacketHandler : IPacketHandler
+{
+ private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
+
+ private readonly MinecraftClient _client;
+ private readonly MinecraftData _data;
+
+ public ConfigurationPacketHandler(MinecraftClient client, MinecraftData data)
+ {
+ this._client = client;
+ this._data = data;
+ }
+
+ public Task HandleIncoming(IPacket packet)
+ {
+ return packet switch
+ {
+ DisconnectPacket disconnect => HandleDisconnect(disconnect),
+ FinishConfigurationPacket finishConfiguration => HandleFinishConfiguration(finishConfiguration),
+ KeepAlivePacket keepAlive => HandleKeepAlive(keepAlive),
+ PingPacket ping => HandlePing(ping),
+
+ _ => Task.CompletedTask
+ };
+ }
+
+ public Task HandleOutgoing(IPacket packet)
+ {
+ if (packet is Serverbound.Configuration.FinishConfigurationPacket)
+ {
+ this._client.UpdateGameState(GameState.Play);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public bool HandlesIncoming(PacketType type)
+ => type is PacketType.CB_Configuration_Disconnect
+ or PacketType.CB_Configuration_FinishConfiguration
+ or PacketType.CB_Configuration_KeepAlive
+ or PacketType.CB_Configuration_Ping;
+
+
+ private Task HandleDisconnect(DisconnectPacket packet)
+ {
+ _ = Task.Run(() => this._client.Disconnect(packet.Reason.Json));
+ return Task.CompletedTask;
+ }
+
+ private Task HandleFinishConfiguration(FinishConfigurationPacket packet)
+ {
+ _ = this._client.SendPacket(new Serverbound.Configuration.FinishConfigurationPacket());
+ return Task.CompletedTask;
+ }
+
+ private Task HandleKeepAlive(KeepAlivePacket packet)
+ {
+ this._client.SendPacket(new Serverbound.Configuration.KeepAlivePacket(packet.KeepAliveId));
+ return Task.CompletedTask;
+ }
+
+ private Task HandlePing(PingPacket packet)
+ {
+ this._client.SendPacket(new Serverbound.Configuration.PongPacket(packet.Id));
+ return Task.CompletedTask;
+ }
+}
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/Server/HandshakePacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Server/HandshakePacketHandler.cs
new file mode 100644
index 00000000..57582afa
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Server/HandshakePacketHandler.cs
@@ -0,0 +1,52 @@
+using MineSharp.Data.Protocol;
+using MineSharp.Protocol.Exceptions;
+using MineSharp.Protocol.Packets.Handlers.Client;
+using MineSharp.Protocol.Packets.Serverbound.Handshaking;
+
+namespace MineSharp.Protocol.Packets.Handlers.Server;
+
+internal class HandshakePacketHandler : IPacketHandler
+{
+ private MinecraftClient _client;
+
+ public HandshakePacketHandler(MinecraftClient client)
+ {
+ _client = client;
+ }
+
+
+ public bool HandlesIncoming(PacketType type) {
+ return type switch
+ {
+ PacketType.SB_Handshake_SetProtocol => true,
+ _ => false
+ };
+ }
+
+
+ public Task HandleIncoming(IPacket packet)
+ {
+ return packet switch
+ {
+ HandshakePacket handshakePacket => HandleHandshake(handshakePacket)
+ };
+ }
+
+ public Task HandleOutgoing(IPacket packet)
+ {
+ return Task.CompletedTask;
+ }
+
+
+ private Task HandleHandshake(HandshakePacket packet)
+ {
+ if (_client.Data.Version.Protocol != packet.ProtocolVersion)
+ {
+ _client.Disconnect();
+ return Task.CompletedTask;
+ }
+ _client.HandshakeUpdateHostAndPort(packet.Host, packet.Port);
+ _client.UpdateGameState(packet.NextState);
+ return Task.CompletedTask;
+ }
+}
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/Server/LoginPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Server/LoginPacketHandler.cs
new file mode 100644
index 00000000..aa302cff
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Server/LoginPacketHandler.cs
@@ -0,0 +1,123 @@
+using MineSharp.Auth.Exceptions;
+using MineSharp.Core.Common;
+using MineSharp.Core.Common.Protocol;
+using MineSharp.Data;
+using MineSharp.Data.Protocol;
+using MineSharp.Protocol.Cryptography;
+using MineSharp.Protocol.Packets.Clientbound.Login;
+using MineSharp.Protocol.Packets.Serverbound.Login;
+using NLog;
+using System.Diagnostics;
+using System.Security.Cryptography;
+
+namespace MineSharp.Protocol.Packets.Handlers.Server;
+
+internal class LoginPacketHandler : IPacketHandler
+{
+ private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
+
+ private readonly MinecraftClient _client;
+ private readonly MinecraftData _data;
+
+ public LoginPacketHandler(MinecraftClient client, MinecraftData data)
+ {
+ this._client = client;
+ this._data = data;
+ }
+
+ public Task HandleIncoming(IPacket packet)
+ {
+ return packet switch
+ {
+ DisconnectPacket disconnect => HandleDisconnect(disconnect),
+ EncryptionRequestPacket encryption => HandleEncryptionRequest(encryption),
+ SetCompressionPacket compression => HandleSetCompression(compression),
+ LoginSuccessPacket success => HandleLoginSuccess(success),
+ _ => throw new UnreachableException()
+ };
+ }
+
+ public Task HandleOutgoing(IPacket packet)
+ => Task.CompletedTask;
+
+ public bool HandlesIncoming(PacketType type)
+ => type is PacketType.CB_Login_Disconnect
+ or PacketType.CB_Login_EncryptionBegin
+ or PacketType.CB_Login_Compress
+ or PacketType.CB_Login_LoginPluginRequest
+ or PacketType.CB_Login_Success;
+
+ private Task HandleDisconnect(DisconnectPacket packet)
+ {
+ _ = Task.Run(() => this._client.Disconnect(packet.Reason.Json));
+ return Task.CompletedTask;
+ }
+
+ private async Task HandleEncryptionRequest(EncryptionRequestPacket packet)
+ {
+ var aes = Aes.Create();
+ aes.KeySize = 128;
+ aes.GenerateKey();
+
+ var hex = EncryptionHelper.ComputeHash(packet.ServerId, aes.Key, packet.PublicKey);
+
+ // Authenticate
+ if (this._client.Session.OnlineSession)
+ {
+ if (!await this._client.Api!.JoinServer(hex, this._client.Session.SessionToken, this._client.Session.UUID))
+ {
+ throw new MineSharpAuthException("Error trying to authenticate with Mojang");
+ }
+ }
+
+ var rsa = EncryptionHelper.DecodePublicKey(packet.PublicKey!);
+ if (rsa == null)
+ throw new Exception("Could not decode public key");
+
+ var sharedSecret = rsa.Encrypt(aes.Key, RSAEncryptionPadding.Pkcs1);
+ var encVerToken = rsa.Encrypt(packet.VerifyToken!, RSAEncryptionPadding.Pkcs1);
+
+ EncryptionResponsePacket response;
+ if (ProtocolVersion.IsBetween(this._data.Version.Protocol, ProtocolVersion.V_1_19, ProtocolVersion.V_1_19_2)
+ && this._client.Session.OnlineSession
+ && this._client.Session.Certificate is not null)
+ {
+ var salt = (long)RandomNumberGenerator.GetInt32(int.MaxValue) << 32 | (uint)RandomNumberGenerator.GetInt32(int.MaxValue);
+
+ var signData = new PacketBuffer(this._data.Version.Protocol >= ProtocolVersion.V_1_20_2);
+ signData.WriteBytes(packet.VerifyToken);
+ signData.WriteLong(salt);
+
+ var signed = this._client.Session.Certificate.RsaPrivate.SignData(signData.GetBuffer(), HashAlgorithmName.SHA256,
+ RSASignaturePadding.Pkcs1);
+ var crypto = new EncryptionResponsePacket.CryptoContainer(salt, signed);
+
+ response = new EncryptionResponsePacket(sharedSecret, null, crypto);
+ }
+ else
+ response = new EncryptionResponsePacket(sharedSecret, encVerToken, null);
+
+ _ = this._client.SendPacket(response)
+ .ContinueWith(_ => this._client.EnableEncryption(aes.Key));
+ }
+
+ private Task HandleSetCompression(SetCompressionPacket packet)
+ {
+ Logger.Debug($"Enabling compression, threshold = {packet.Threshold}.");
+ this._client.SetCompression(packet.Threshold);
+ return Task.CompletedTask;
+ }
+
+ private Task HandleLoginSuccess(LoginSuccessPacket packet)
+ {
+ if (this._data.Version.Protocol < ProtocolVersion.V_1_20_2)
+ {
+ this._client.UpdateGameState(GameState.Play);
+ return Task.CompletedTask;
+ }
+
+ _ = this._client.SendPacket(new AcknowledgeLoginPacket())
+ .ContinueWith(_ => this._client.UpdateGameState(GameState.Configuration));
+ return Task.CompletedTask;
+ }
+}
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/Server/PlayPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Server/PlayPacketHandler.cs
new file mode 100644
index 00000000..c4caa21e
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Server/PlayPacketHandler.cs
@@ -0,0 +1,63 @@
+using MineSharp.Data;
+using MineSharp.Data.Protocol;
+using MineSharp.Protocol.Packets.Clientbound.Play;
+using MineSharp.Protocol.Packets.Serverbound.Play;
+using KeepAlivePacket = MineSharp.Protocol.Packets.Clientbound.Play.KeepAlivePacket;
+
+namespace MineSharp.Protocol.Packets.Handlers.Server;
+
+internal class PlayPacketHandler : IPacketHandler
+{
+ private MinecraftClient _client;
+ private MinecraftData _data;
+
+ public PlayPacketHandler(MinecraftClient client, MinecraftData data)
+ {
+ this._client = client;
+ this._data = data;
+ }
+
+ public Task HandleIncoming(IPacket packet)
+ {
+ return packet switch
+ {
+ KeepAlivePacket keepAlive => HandleKeepAlive(keepAlive),
+ BundleDelimiterPacket bundleDelimiter => HandleBundleDelimiter(bundleDelimiter),
+ PingPacket ping => HandlePing(ping),
+ DisconnectPacket disconnect => HandleDisconnect(disconnect),
+ _ => Task.CompletedTask
+ };
+ }
+
+ public Task HandleOutgoing(IPacket packet)
+ {
+ return Task.CompletedTask;
+ }
+
+ public bool HandlesIncoming(PacketType type)
+ => type is PacketType.CB_Play_KeepAlive or PacketType.CB_Play_BundleDelimiter or PacketType.CB_Play_Ping or PacketType.CB_Play_KickDisconnect;
+
+ private Task HandleKeepAlive(KeepAlivePacket packet)
+ {
+ this._client.SendPacket(new Serverbound.Play.KeepAlivePacket(packet.KeepAliveId));
+ return Task.CompletedTask;
+ }
+
+ private Task HandleBundleDelimiter(BundleDelimiterPacket bundleDelimiter)
+ {
+ this._client.HandleBundleDelimiter();
+ return Task.CompletedTask;
+ }
+
+ private Task HandlePing(PingPacket ping)
+ {
+ this._client.SendPacket(new PongPacket(ping.Id));
+ return Task.CompletedTask;
+ }
+
+ private Task HandleDisconnect(DisconnectPacket packet)
+ {
+ _ = Task.Run(() => this._client.Disconnect(packet.Reason.Json));
+ return Task.CompletedTask;
+ }
+}
diff --git a/Components/MineSharp.Protocol/Packets/Handlers/Server/StatusPacketHandler.cs b/Components/MineSharp.Protocol/Packets/Handlers/Server/StatusPacketHandler.cs
new file mode 100644
index 00000000..577f20e4
--- /dev/null
+++ b/Components/MineSharp.Protocol/Packets/Handlers/Server/StatusPacketHandler.cs
@@ -0,0 +1,17 @@
+using MineSharp.Data.Protocol;
+
+namespace MineSharp.Protocol.Packets.Handlers.Server;
+
+internal class StatusPacketHandler : IPacketHandler
+{
+ private MinecraftClient _client;
+
+ public StatusPacketHandler(MinecraftClient client)
+ {
+ this._client = client;
+ }
+
+ public Task HandleIncoming(IPacket packet) => Task.CompletedTask;
+ public Task HandleOutgoing(IPacket packet) => Task.CompletedTask;
+ public bool HandlesIncoming(PacketType type) => false;
+}