Packets refactoring to async

This commit is contained in:
Anon 2026-04-04 22:28:05 +02:00
parent 4d800a31fc
commit 5d03cd4fd1
10 changed files with 611 additions and 312 deletions

View file

@ -21,6 +21,7 @@ using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Protocol.Handlers.packet.s2c;
using MinecraftClient.Protocol.Handlers.PacketPalettes;
using MinecraftClient.Protocol.Message;
using MinecraftClient.Protocol.PacketPipeline;
using MinecraftClient.Protocol.ProfileKey;
using MinecraftClient.Protocol.Session;
using MinecraftClient.Proxy;
@ -91,7 +92,7 @@ namespace MinecraftClient.Protocol.Handlers
private readonly int rawProtocolVersion;
private int currentDimension;
private bool isOnlineMode = false;
private readonly BlockingCollection<Tuple<int, Queue<byte>>> packetQueue = new();
private readonly BlockingCollection<IncomingPacket> packetQueue = new();
private readonly Dictionary<string, bool> legacyAchievementProgress = new(StringComparer.Ordinal);
private float LastYaw, LastPitch;
private double lastSentX, lastSentY, lastSentZ;
@ -309,8 +310,7 @@ namespace MinecraftClient.Protocol.Handlers
if (packetQueue.TryTake(out var packetInfo, 1))
{
var (packetId, packetData) = packetInfo;
HandlePacket(packetId, packetData);
HandlePacket(packetInfo.PacketId, packetInfo.CreateReader());
continue;
}
@ -393,27 +393,27 @@ namespace MinecraftClient.Protocol.Handlers
/// </summary>
/// <param name="packetId">will contain packet ID</param>
/// <param name="packetData">will contain raw packet Data</param>
internal Tuple<int, Queue<byte>> ReadNextPacket()
internal IncomingPacket ReadNextPacket()
{
var (packetId, packetData) = socketWrapper.GetNextPacket(
IncomingPacket packet = socketWrapper.GetNextPacket(
protocolVersion >= MC_1_8_Version ? compression_treshold : -1,
dataTypes);
if (handler.GetNetworkPacketCaptureEnabled())
handler.OnNetworkPacket(packetId, packetData.ToList(), currentState == CurrentState.Login, true);
handler.OnNetworkPacket(packet.PacketId, packet.Payload.ToList(), currentState == CurrentState.Login, true);
return new(packetId, packetData);
return packet;
}
internal async Task<Tuple<int, Queue<byte>>> ReadNextPacketAsync(CancellationToken cancellationToken)
internal async Task<IncomingPacket> ReadNextPacketAsync(CancellationToken cancellationToken)
{
var (packetId, packetData) = await socketWrapper.GetNextPacketAsync(
IncomingPacket packet = await socketWrapper.GetNextPacketAsync(
protocolVersion >= MC_1_8_Version ? compression_treshold : -1,
dataTypes,
cancellationToken);
if (handler.GetNetworkPacketCaptureEnabled())
handler.OnNetworkPacket(packetId, packetData.ToList(), currentState == CurrentState.Login, true);
handler.OnNetworkPacket(packet.PacketId, packet.Payload.ToList(), currentState == CurrentState.Login, true);
return new(packetId, packetData);
return packet;
}
/// <summary>
@ -422,11 +422,9 @@ namespace MinecraftClient.Protocol.Handlers
/// <param name="packetId">Packet ID</param>
/// <param name="packetData">Packet contents</param>
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
internal bool HandlePacket(int packetId, Queue<byte> packetData)
internal bool HandlePacket(int packetId, PacketReader packetData)
{
// This copy is necessary because by the time we get to the catch block,
// the packetData queue will have been processed and the data will be lost
var _copy = packetData.ToArray();
byte[] _copy = packetData.GetRawData();
try
{
@ -488,11 +486,11 @@ namespace MinecraftClient.Protocol.Handlers
break;
case ConfigurationPacketTypesIn.KeepAlive:
SendPacket(ConfigurationPacketTypesOut.KeepAlive, packetData);
SendPacket(ConfigurationPacketTypesOut.KeepAlive, packetData.CopyRemaining());
break;
case ConfigurationPacketTypesIn.Ping:
SendPacket(ConfigurationPacketTypesOut.Pong, packetData);
SendPacket(ConfigurationPacketTypesOut.Pong, packetData.CopyRemaining());
break;
case ConfigurationPacketTypesIn.RegistryData:
@ -674,7 +672,7 @@ namespace MinecraftClient.Protocol.Handlers
return true;
}
public void HandleResourcePackPacket(Queue<byte> packetData)
public void HandleResourcePackPacket(PacketReader packetData)
{
var uuid = Guid.Empty;
@ -722,17 +720,17 @@ namespace MinecraftClient.Protocol.Handlers
}
}
private bool HandlePlayPackets(int packetId, Queue<byte> packetData)
private bool HandlePlayPackets(int packetId, PacketReader packetData)
{
switch (packetPalette.GetIncomingTypeById(packetId))
{
case PacketTypesIn.KeepAlive: // Keep Alive (Play)
SendPacket(PacketTypesOut.KeepAlive, packetData);
SendPacket(PacketTypesOut.KeepAlive, packetData.CopyRemaining());
handler.OnServerKeepAlive();
break;
case PacketTypesIn.Ping:
SendPacket(PacketTypesOut.Pong, packetData);
SendPacket(PacketTypesOut.Pong, packetData.CopyRemaining());
break;
case PacketTypesIn.JoinGame:
@ -1602,7 +1600,7 @@ namespace MinecraftClient.Protocol.Handlers
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap,
currentDimension == 0, chunksContinuous, currentDimension,
new Queue<byte>(decompressed));
new PacketReader(decompressed));
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
}
else
@ -1984,7 +1982,7 @@ namespace MinecraftClient.Protocol.Handlers
hasSkyLight = dataTypes.ReadNextBool(packetData);
var compressed = dataTypes.ReadData(compressedDataSize, packetData);
var decompressed = ZlibUtils.Decompress(compressed);
chunkData = new Queue<byte>(decompressed);
chunkData = new PacketReader(decompressed);
}
else
{
@ -2297,7 +2295,7 @@ namespace MinecraftClient.Protocol.Handlers
// Length is unneeded as the whole remaining packetData is the entire payload of the packet.
if (protocolVersion < MC_1_8_Version)
pForge.ReadNextVarShort(packetData);
handler.OnPluginChannelMessage(channel, packetData.ToArray());
handler.OnPluginChannelMessage(channel, packetData.CopyRemaining());
return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
case PacketTypesIn.Disconnect:
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick,
@ -3323,7 +3321,7 @@ namespace MinecraftClient.Protocol.Handlers
/// Read a Holder&lt;SoundEvent&gt; from packet data and return its key when inline.
/// Returns null when the holder is a registry reference.
/// </summary>
private string? ReadSoundEventHolderName(Queue<byte> packetData)
private string? ReadSoundEventHolderName(PacketReader packetData)
{
int soundHolderId = dataTypes.ReadNextVarInt(packetData);
if (soundHolderId != 0)
@ -3339,7 +3337,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Handle the Statistics packet for pre-1.12 legacy achievements.
/// </summary>
private void HandleLegacyStatistics(Queue<byte> packetData)
private void HandleLegacyStatistics(PacketReader packetData)
{
int statCount = dataTypes.ReadNextVarInt(packetData);
@ -3370,7 +3368,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Handle the Advancements packet (1.12+).
/// </summary>
private void HandleAdvancements(Queue<byte> packetData)
private void HandleAdvancements(PacketReader packetData)
{
bool reset = dataTypes.ReadNextBool(packetData);
@ -3541,14 +3539,14 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Handle the SelectAdvancementTab packet.
/// </summary>
private void HandleSelectAdvancementTab(Queue<byte> packetData)
private void HandleSelectAdvancementTab(PacketReader packetData)
{
bool hasTab = dataTypes.ReadNextBool(packetData);
string? tabId = hasTab ? dataTypes.ReadNextString(packetData) : null;
handler.OnSelectAdvancementTab(tabId);
}
private void HandleUnlockRecipes(Queue<byte> packetData)
private void HandleUnlockRecipes(PacketReader packetData)
{
int action = dataTypes.ReadNextVarInt(packetData);
if (!SkipRecipeBookSettings(packetData))
@ -3576,7 +3574,7 @@ namespace MinecraftClient.Protocol.Handlers
}
}
private void HandleRecipeBookAdd(Queue<byte> packetData)
private void HandleRecipeBookAdd(PacketReader packetData)
{
int entryCount = dataTypes.ReadNextVarInt(packetData);
RecipeBookRecipeEntry[] recipeEntries = new RecipeBookRecipeEntry[entryCount];
@ -3593,7 +3591,7 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnRecipeBookAdd(recipeEntries, replace);
}
private string[] ReadRecipeBookRecipeIds(Queue<byte> packetData)
private string[] ReadRecipeBookRecipeIds(PacketReader packetData)
{
int recipeCount = dataTypes.ReadNextVarInt(packetData);
string[] recipeIds = new string[recipeCount];
@ -3604,7 +3602,7 @@ namespace MinecraftClient.Protocol.Handlers
return recipeIds;
}
private string[] ReadRecipeBookDisplayIds(Queue<byte> packetData)
private string[] ReadRecipeBookDisplayIds(PacketReader packetData)
{
int recipeCount = dataTypes.ReadNextVarInt(packetData);
string[] recipeIds = new string[recipeCount];
@ -3615,7 +3613,7 @@ namespace MinecraftClient.Protocol.Handlers
return recipeIds;
}
private RecipeBookRecipeEntry ReadRecipeBookDisplayEntry(Queue<byte> packetData)
private RecipeBookRecipeEntry ReadRecipeBookDisplayEntry(PacketReader packetData)
{
int displayId = dataTypes.ReadNextVarInt(packetData);
string resultLabel = ReadRecipeDisplayResultLabel(packetData);
@ -3629,7 +3627,7 @@ namespace MinecraftClient.Protocol.Handlers
return new RecipeBookRecipeEntry(commandId, displayText);
}
private string ReadRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadRecipeDisplayResultLabel(PacketReader packetData)
{
int displayType = dataTypes.ReadNextVarInt(packetData);
return displayType switch
@ -3643,7 +3641,7 @@ namespace MinecraftClient.Protocol.Handlers
};
}
private string ReadShapelessRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadShapelessRecipeDisplayResultLabel(PacketReader packetData)
{
int ingredientCount = dataTypes.ReadNextVarInt(packetData);
for (int i = 0; i < ingredientCount; i++)
@ -3654,7 +3652,7 @@ namespace MinecraftClient.Protocol.Handlers
return result;
}
private string ReadShapedRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadShapedRecipeDisplayResultLabel(PacketReader packetData)
{
_ = dataTypes.ReadNextVarInt(packetData); // width
_ = dataTypes.ReadNextVarInt(packetData); // height
@ -3667,7 +3665,7 @@ namespace MinecraftClient.Protocol.Handlers
return result;
}
private string ReadFurnaceRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadFurnaceRecipeDisplayResultLabel(PacketReader packetData)
{
_ = ReadSlotDisplayLabel(packetData); // ingredient
_ = ReadSlotDisplayLabel(packetData); // fuel
@ -3678,7 +3676,7 @@ namespace MinecraftClient.Protocol.Handlers
return result;
}
private string ReadStonecutterRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadStonecutterRecipeDisplayResultLabel(PacketReader packetData)
{
_ = ReadSlotDisplayLabel(packetData); // input
string result = ReadSlotDisplayLabel(packetData);
@ -3686,7 +3684,7 @@ namespace MinecraftClient.Protocol.Handlers
return result;
}
private string ReadSmithingRecipeDisplayResultLabel(Queue<byte> packetData)
private string ReadSmithingRecipeDisplayResultLabel(PacketReader packetData)
{
_ = ReadSlotDisplayLabel(packetData); // template
_ = ReadSlotDisplayLabel(packetData); // base
@ -3696,7 +3694,7 @@ namespace MinecraftClient.Protocol.Handlers
return result;
}
private string ReadSlotDisplayLabel(Queue<byte> packetData)
private string ReadSlotDisplayLabel(PacketReader packetData)
{
int slotDisplayType = dataTypes.ReadNextVarInt(packetData);
@ -3739,7 +3737,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Reads a with_any_potion slot display (26.1+): contains a nested SlotDisplay.
/// </summary>
private string ReadWithAnyPotionSlotDisplayLabel(Queue<byte> packetData)
private string ReadWithAnyPotionSlotDisplayLabel(PacketReader packetData)
{
return ReadSlotDisplayLabel(packetData);
}
@ -3747,7 +3745,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Reads an only_with_component slot display (26.1+): contains a nested SlotDisplay and a DataComponentType VarInt ID.
/// </summary>
private string ReadOnlyWithComponentSlotDisplayLabel(Queue<byte> packetData)
private string ReadOnlyWithComponentSlotDisplayLabel(PacketReader packetData)
{
string sourceLabel = ReadSlotDisplayLabel(packetData);
_ = dataTypes.ReadNextVarInt(packetData); // DataComponentType registry id
@ -3757,14 +3755,14 @@ namespace MinecraftClient.Protocol.Handlers
/// <summary>
/// Reads a dyed slot display (26.1+): contains two nested SlotDisplays (dye + target).
/// </summary>
private string ReadDyedSlotDisplayLabel(Queue<byte> packetData)
private string ReadDyedSlotDisplayLabel(PacketReader packetData)
{
_ = ReadSlotDisplayLabel(packetData); // dye
string targetLabel = ReadSlotDisplayLabel(packetData); // target
return targetLabel;
}
private string ReadSmithingTrimSlotDisplayLabel(Queue<byte> packetData)
private string ReadSmithingTrimSlotDisplayLabel(PacketReader packetData)
{
string baseLabel = ReadSlotDisplayLabel(packetData);
_ = ReadSlotDisplayLabel(packetData); // material
@ -3772,14 +3770,14 @@ namespace MinecraftClient.Protocol.Handlers
return baseLabel;
}
private string ReadWithRemainderSlotDisplayLabel(Queue<byte> packetData)
private string ReadWithRemainderSlotDisplayLabel(PacketReader packetData)
{
string inputLabel = ReadSlotDisplayLabel(packetData);
_ = ReadSlotDisplayLabel(packetData); // remainder
return inputLabel;
}
private string ReadCompositeSlotDisplayLabel(Queue<byte> packetData)
private string ReadCompositeSlotDisplayLabel(PacketReader packetData)
{
int optionCount = dataTypes.ReadNextVarInt(packetData);
string label = "Composite";
@ -3798,12 +3796,12 @@ namespace MinecraftClient.Protocol.Handlers
/// Read an ItemStackTemplate (26.1+) which encodes fields in a different order
/// than ItemStack: item_id (VarInt), count (VarInt), DataComponentPatch.
/// </summary>
private string ReadItemStackTemplateLabel(Queue<byte> packetData)
private string ReadItemStackTemplateLabel(PacketReader packetData)
{
return dataTypes.ReadNextItemStackTemplate(packetData, itemPalette).GetTypeString();
}
private void SkipOptionalCraftingRequirements(Queue<byte> packetData)
private void SkipOptionalCraftingRequirements(PacketReader packetData)
{
if (!dataTypes.ReadNextBool(packetData))
return;
@ -3813,7 +3811,7 @@ namespace MinecraftClient.Protocol.Handlers
SkipItemHolderSet(packetData);
}
private void SkipItemHolderSet(Queue<byte> packetData)
private void SkipItemHolderSet(PacketReader packetData)
{
int entryCount = dataTypes.ReadNextVarInt(packetData) - 1;
if (entryCount == -1)
@ -3826,12 +3824,12 @@ namespace MinecraftClient.Protocol.Handlers
_ = dataTypes.ReadNextVarInt(packetData);
}
private bool SkipRecipeBookSettings(Queue<byte> packetData)
private bool SkipRecipeBookSettings(PacketReader packetData)
{
// MC 1.13 uses 4 booleans for the crafting/smelting recipe book states.
// MC 1.14+ expands this to 8 booleans by adding blast furnace and smoker states.
int boolCount = protocolVersion >= MC_1_14_Version ? 8 : 4;
if (packetData.Count < boolCount)
if (packetData.RemainingLength < boolCount)
return false;
for (int i = 0; i < boolCount; i++)
@ -3896,9 +3894,9 @@ namespace MinecraftClient.Protocol.Handlers
SendPacket(packetPalette.GetOutgoingIdByType(packet), packetData);
}
private void ProcessChunkBlockEntityData(int chunkX, int chunkZ, Queue<byte> packetData)
private void ProcessChunkBlockEntityData(int chunkX, int chunkZ, PacketReader packetData)
{
if (protocolVersion < MC_1_17_Version || packetData.Count == 0)
if (protocolVersion < MC_1_17_Version || packetData.RemainingLength == 0)
return;
int blockEntityCount = dataTypes.ReadNextVarInt(packetData);
@ -4057,7 +4055,9 @@ namespace MinecraftClient.Protocol.Handlers
// 3. Encryption Request - 9. Login Acknowledged
while (true)
{
var (packetId, packetData) = await ReadNextPacketAsync(CancellationToken.None);
IncomingPacket packet = await ReadNextPacketAsync(CancellationToken.None);
int packetId = packet.PacketId;
PacketReader packetData = packet.CreateReader();
switch (packetId)
{
@ -4195,7 +4195,9 @@ namespace MinecraftClient.Protocol.Handlers
int loopPrevention = ushort.MaxValue;
while (true)
{
var (packetId, packetData) = await ReadNextPacketAsync(CancellationToken.None);
IncomingPacket packet = await ReadNextPacketAsync(CancellationToken.None);
int packetId = packet.PacketId;
PacketReader packetData = packet.CreateReader();
if (packetId < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150)
{
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost,
@ -4356,11 +4358,12 @@ namespace MinecraftClient.Protocol.Handlers
var statusRequest = DataTypes.GetVarInt(0);
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
var (statusPacketId, packetData) = socketWrapper.GetNextPacket(-1, dataTypes);
if (statusPacketId != 0x00)
IncomingPacket statusPacket = socketWrapper.GetNextPacket(-1, dataTypes);
if (statusPacket.PacketId != 0x00)
return false;
// Get the Json data
var packetData = statusPacket.CreateReader();
var result = dataTypes.ReadNextString(packetData);
if (Config.Logging.DebugMessages)
@ -4436,9 +4439,10 @@ namespace MinecraftClient.Protocol.Handlers
var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload));
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest));
var (pongPacketId, pongPacketData) = socketWrapper.GetNextPacket(-1, dataTypes);
if (pongPacketId == 0x01)
IncomingPacket pongPacket = socketWrapper.GetNextPacket(-1, dataTypes);
if (pongPacket.PacketId == 0x01)
{
var pongPacketData = pongPacket.CreateReader();
long pongPayload = dataTypes.ReadNextLong(pongPacketData);
pingMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - pingPayload;
}