mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Packets refactoring to async
This commit is contained in:
parent
4d800a31fc
commit
5d03cd4fd1
10 changed files with 611 additions and 312 deletions
|
|
@ -2289,34 +2289,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
return client.GetInventories().Keys.Where(id => id > 0).DefaultIfEmpty(0).Max();
|
||||
}
|
||||
|
||||
private static bool WaitForContainerOpen(McClient client, ISet<int> beforeIds, int waitMs, out int inventoryId, out Container? inventory)
|
||||
{
|
||||
inventoryId = 0;
|
||||
inventory = null;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
(int activeId, Container? activeInventory) state = client.InvokeOnMainThread(() =>
|
||||
{
|
||||
int activeId = GetActiveContainerId(client);
|
||||
Container? activeInventory = activeId > 0 ? client.GetInventory(activeId) : null;
|
||||
return (activeId, activeInventory);
|
||||
});
|
||||
|
||||
if (state.activeId > 0 && (!beforeIds.Contains(state.activeId) || beforeIds.Count == 0) && state.activeInventory is not null)
|
||||
{
|
||||
inventoryId = state.activeId;
|
||||
inventory = state.activeInventory;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForContainerOpenAsync(McClient client, ISet<int> beforeIds, int waitMs, Action<ContainerOpenState> onOpened)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
|
|
@ -2342,22 +2314,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
}
|
||||
|
||||
private static bool WaitForContainerClose(McClient client, int inventoryId, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
bool stillOpen = client.InvokeOnMainThread(() => client.GetInventories().ContainsKey(inventoryId));
|
||||
if (!stillOpen)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForContainerCloseAsync(McClient client, int inventoryId, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
|
|
@ -2453,14 +2409,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
: 0;
|
||||
}
|
||||
|
||||
private static bool TryDropInventorySlotItems(McClient client, int inventoryId, Container inventory, int slotId, ItemType itemType, int dropCount, out int droppedCount)
|
||||
{
|
||||
(bool success, int actualDroppedCount) =
|
||||
TryDropInventorySlotItemsAsync(client, inventoryId, inventory, slotId, itemType, dropCount).GetAwaiter().GetResult();
|
||||
droppedCount = actualDroppedCount;
|
||||
return success;
|
||||
}
|
||||
|
||||
private static int CountItemInRange(Container inventory, ItemType itemType, int startSlot, int endSlot)
|
||||
{
|
||||
return inventory.Items
|
||||
|
|
@ -2827,115 +2775,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
}
|
||||
|
||||
private static bool WaitForCursorItem(McClient client, ItemType itemType, int waitMs, out Item? cursorItem)
|
||||
{
|
||||
cursorItem = null;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
if (TryGetCursorItem(client, out cursorItem) && cursorItem is not null && cursorItem.Type == itemType)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitForCursorClear(McClient client, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
if (!TryGetCursorItem(client, out _))
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitForPlacement(McClient client, int inventoryId, int targetSlot, ItemType itemType, int beforeTargetCount, int beforeCursorCount, int placedCount)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(DefaultInventoryActionWaitMs);
|
||||
while (true)
|
||||
{
|
||||
bool targetUpdated = false;
|
||||
bool cursorUpdated = false;
|
||||
|
||||
Container? inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId));
|
||||
if (inventory is not null)
|
||||
{
|
||||
int currentTargetCount = GetSlotItemCount(inventory, targetSlot, itemType);
|
||||
targetUpdated = currentTargetCount >= beforeTargetCount + placedCount;
|
||||
}
|
||||
|
||||
if (placedCount >= beforeCursorCount)
|
||||
{
|
||||
cursorUpdated = !TryGetCursorItem(client, out _);
|
||||
}
|
||||
else if (TryGetCursorItem(client, out Item? cursorItem) && cursorItem is not null && cursorItem.Type == itemType)
|
||||
{
|
||||
cursorUpdated = cursorItem.Count <= beforeCursorCount - placedCount;
|
||||
}
|
||||
|
||||
if (targetUpdated && cursorUpdated)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitForSlotItemCount(McClient client, int inventoryId, int slotId, ItemType itemType, Func<int, bool> predicate, int waitMs, out Container? inventory, out int itemCount)
|
||||
{
|
||||
inventory = null;
|
||||
itemCount = 0;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
inventory = client.GetInventory(inventoryId);
|
||||
if (inventory is not null)
|
||||
{
|
||||
itemCount = GetSlotItemCount(inventory, slotId, itemType);
|
||||
if (predicate(itemCount))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitForRangeCount(McClient client, int inventoryId, ItemType itemType, int startSlot, int endSlot, Func<int, bool> predicate, int waitMs, out Container? inventory, out int itemCount)
|
||||
{
|
||||
inventory = null;
|
||||
itemCount = 0;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId));
|
||||
if (inventory is not null)
|
||||
{
|
||||
itemCount = CountItemInRange(inventory, itemType, startSlot, endSlot);
|
||||
if (predicate(itemCount))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<NearbyPlayerSnapshot> BuildTrackedPlayerSnapshots(McClient client, bool includeSelf)
|
||||
{
|
||||
Location playerLocation = client.GetCurrentLocation();
|
||||
|
|
@ -3121,25 +2960,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
return new string(buffer);
|
||||
}
|
||||
|
||||
private static bool WaitForArrival(McClient client, Location goal, int waitMs, double tolerance, out Location? finalLocation)
|
||||
{
|
||||
finalLocation = null;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
Location location = client.InvokeOnMainThread(client.GetCurrentLocation);
|
||||
finalLocation = location;
|
||||
double distance = GetDistance(location, goal);
|
||||
if (distance <= tolerance)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<(bool Arrived, Location FinalLocation)> WaitForArrivalAsync(McClient client, Location goal, int waitMs, double tolerance)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
|
|
@ -3186,24 +3006,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
return Math.Clamp(timeoutMs, MinPathQueryTimeoutMs, MaxPathQueryTimeoutMs);
|
||||
}
|
||||
|
||||
private static bool WaitForBlockChange(McClient client, Location target, Block beforeBlock, int waitMs, out Block afterBlock)
|
||||
{
|
||||
afterBlock = beforeBlock;
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
Block current = client.InvokeOnMainThread(() => client.GetWorld().GetBlock(target));
|
||||
afterBlock = current;
|
||||
if (!AreEquivalentBlocks(current, beforeBlock))
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<(bool Changed, Block AfterBlock)> WaitForBlockChangeAsync(McClient client, Location target, Block beforeBlock, int waitMs)
|
||||
{
|
||||
Block afterBlock = beforeBlock;
|
||||
|
|
@ -3337,22 +3139,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
.ToArray();
|
||||
}
|
||||
|
||||
private static bool WaitForEntityRemoval(McClient client, int entityId, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
bool exists = client.InvokeOnMainThread(() => client.GetEntities().ContainsKey(entityId));
|
||||
if (!exists)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetInventoryItemCount(McClient client, ItemType itemType)
|
||||
{
|
||||
Container? inventory = client.GetInventory(0);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using MinecraftClient.Inventory;
|
|||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Mapping.EntityPalettes;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
|
|
@ -44,6 +45,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte[] ReadData(int offset, PacketReader reader)
|
||||
{
|
||||
return reader.ReadData(offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read some data from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -56,6 +63,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
dest[i] = cache.Dequeue();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void ReadDataReverse(PacketReader reader, Span<byte> dest)
|
||||
{
|
||||
reader.ReadDataReverse(dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove some data from the cache
|
||||
/// </summary>
|
||||
|
|
@ -68,6 +81,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
cache.Dequeue();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void DropData(int offset, PacketReader reader)
|
||||
{
|
||||
reader.Skip(offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -84,6 +103,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
else return "";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public string ReadNextString(PacketReader reader)
|
||||
{
|
||||
int length = ReadNextVarInt(reader);
|
||||
return length > 0 ? Encoding.UTF8.GetString(ReadData(length, reader)) : "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip a string from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -94,6 +120,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
DropData(length, cache);
|
||||
}
|
||||
|
||||
public void SkipNextString(PacketReader reader)
|
||||
{
|
||||
int length = ReadNextVarInt(reader);
|
||||
DropData(length, reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a boolean from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -104,6 +136,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return ReadNextByte(cache) != 0x00;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool ReadNextBool(PacketReader reader)
|
||||
{
|
||||
return ReadNextByte(reader) != 0x00;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a short integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -117,6 +155,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToInt16(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public short ReadNextShort(PacketReader reader)
|
||||
{
|
||||
return reader.ReadInt16BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -130,6 +174,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToInt32(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public int ReadNextInt(PacketReader reader)
|
||||
{
|
||||
return reader.ReadInt32BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a long integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -143,6 +193,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToInt64(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public long ReadNextLong(PacketReader reader)
|
||||
{
|
||||
return reader.ReadInt64BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -156,6 +212,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToUInt16(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public ushort ReadNextUShort(PacketReader reader)
|
||||
{
|
||||
return reader.ReadUInt16BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an unsigned long integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -169,6 +231,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToUInt64(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public ulong ReadNextULong(PacketReader reader)
|
||||
{
|
||||
return unchecked((ulong)reader.ReadInt64BigEndian());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Location encoded as an ulong field and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -199,6 +267,32 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return new Location(x, y, z);
|
||||
}
|
||||
|
||||
public Location ReadNextLocation(PacketReader reader)
|
||||
{
|
||||
ulong locEncoded = ReadNextULong(reader);
|
||||
int x, y, z;
|
||||
if (protocolversion >= Protocol18Handler.MC_1_14_Version)
|
||||
{
|
||||
x = (int)(locEncoded >> 38);
|
||||
y = (int)(locEncoded & 0xFFF);
|
||||
z = (int)(locEncoded << 26 >> 38);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = (int)(locEncoded >> 38);
|
||||
y = (int)((locEncoded >> 26) & 0xFFF);
|
||||
z = (int)(locEncoded << 38 >> 38);
|
||||
}
|
||||
|
||||
if (x >= 0x02000000)
|
||||
x -= 0x04000000;
|
||||
if (y >= 0x00000800)
|
||||
y -= 0x00001000;
|
||||
if (z >= 0x02000000)
|
||||
z -= 0x04000000;
|
||||
return new Location(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache
|
||||
/// </summary>
|
||||
|
|
@ -212,6 +306,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result;
|
||||
}
|
||||
|
||||
public ushort[] ReadNextUShortsLittleEndian(int amount, PacketReader reader)
|
||||
{
|
||||
byte[] rawValues = ReadData(2 * amount, reader);
|
||||
ushort[] result = new ushort[amount];
|
||||
for (int i = 0; i < amount; i++)
|
||||
result[i] = BitConverter.ToUInt16(rawValues, i * 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a uuid from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -228,6 +331,16 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return guid;
|
||||
}
|
||||
|
||||
public Guid ReadNextUUID(PacketReader reader)
|
||||
{
|
||||
Span<byte> javaUUID = stackalloc byte[16];
|
||||
reader.ReadData(javaUUID);
|
||||
Guid guid = new(javaUUID);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
guid = guid.ToLittleEndian();
|
||||
return guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte array from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -242,6 +355,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return ReadData(len, cache);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte[] ReadNextByteArray(PacketReader reader)
|
||||
{
|
||||
int len = protocolversion >= Protocol18Handler.MC_1_8_Version
|
||||
? ReadNextVarInt(reader)
|
||||
: ReadNextShort(reader);
|
||||
return ReadData(len, reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte array with given length from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -254,6 +376,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return ReadData(length, cache);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte[] ReadNextByteArray(PacketReader reader, int length)
|
||||
{
|
||||
return ReadData(length, reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a length-prefixed array of unsigned long integers and removes it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -268,6 +396,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result;
|
||||
}
|
||||
|
||||
public ulong[] ReadNextULongArray(PacketReader reader)
|
||||
{
|
||||
int len = ReadNextVarInt(reader);
|
||||
ulong[] result = new ulong[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
result[i] = ReadNextULong(reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a double from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -281,6 +418,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToDouble(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public double ReadNextDouble(PacketReader reader)
|
||||
{
|
||||
return BitConverter.Int64BitsToDouble(ReadNextLong(reader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a float from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -294,6 +437,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return BitConverter.ToSingle(rawValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public float ReadNextFloat(PacketReader reader)
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle(ReadNextInt(reader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from the network
|
||||
/// </summary>
|
||||
|
|
@ -357,6 +506,22 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return i;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public int ReadNextVarInt(PacketReader reader)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
byte b;
|
||||
do
|
||||
{
|
||||
b = reader.ReadByte();
|
||||
i |= (b & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
} while ((b & 0x80) == 128);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip a VarInt from a cache of bytes with better performance
|
||||
/// </summary>
|
||||
|
|
@ -369,6 +534,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
break;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void SkipNextVarInt(PacketReader reader)
|
||||
{
|
||||
while (true)
|
||||
if ((ReadNextByte(reader) & 0x80) != 128)
|
||||
break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an "extended short", which is actually an int of some kind, from the cache of bytes.
|
||||
/// This is only done with forge. It looks like it's a normal short, except that if the high
|
||||
|
|
@ -389,6 +562,19 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return ((high & 0xFF) << 15) | low;
|
||||
}
|
||||
|
||||
public int ReadNextVarShort(PacketReader reader)
|
||||
{
|
||||
ushort low = ReadNextUShort(reader);
|
||||
byte high = 0;
|
||||
if ((low & 0x8000) != 0)
|
||||
{
|
||||
low &= 0x7FFF;
|
||||
high = ReadNextByte(reader);
|
||||
}
|
||||
|
||||
return ((high & 0xFF) << 15) | low;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a long from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -415,6 +601,27 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result;
|
||||
}
|
||||
|
||||
public long ReadNextVarLong(PacketReader reader)
|
||||
{
|
||||
int numRead = 0;
|
||||
long result = 0;
|
||||
byte read;
|
||||
do
|
||||
{
|
||||
read = ReadNextByte(reader);
|
||||
long value = (read & 0x7F);
|
||||
result |= (value << (7 * numRead));
|
||||
|
||||
numRead++;
|
||||
if (numRead > 10)
|
||||
{
|
||||
throw new OverflowException("VarLong is too big");
|
||||
}
|
||||
} while ((read & 0x80) != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a single byte from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -426,6 +633,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte ReadNextByte(PacketReader reader)
|
||||
{
|
||||
return reader.ReadByte();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an uncompressed Named Binary Tag blob and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -434,6 +647,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return ReadNextNbt(cache, true);
|
||||
}
|
||||
|
||||
public Dictionary<string, object> ReadNextNbt(PacketReader reader)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextNbt(cache, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an ItemStackTemplate (26.1+) from a cache of bytes.
|
||||
/// Unlike ItemStack, this uses item-first encoding: item_id, count, DataComponentPatch.
|
||||
|
|
@ -465,6 +683,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return item;
|
||||
}
|
||||
|
||||
public Item ReadNextItemStackTemplate(PacketReader reader, ItemPalette itemPalette)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextItemStackTemplate(cache, itemPalette));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a single item slot from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
@ -568,6 +791,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public Item? ReadNextItemSlot(PacketReader reader, ItemPalette itemPalette)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextItemSlot(cache, itemPalette));
|
||||
}
|
||||
|
||||
private void ReadNextDetail(Queue<byte> cache)
|
||||
{
|
||||
var potionEffectId = ReadNextVarInt(cache);
|
||||
|
|
@ -724,6 +952,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return entity;
|
||||
}
|
||||
|
||||
public Entity ReadNextEntity(PacketReader reader, EntityPalette entityPalette, bool living)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextEntity(cache, entityPalette, living));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
|
||||
/// </summary>
|
||||
|
|
@ -1077,6 +1310,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public Dictionary<int, object?> ReadNextMetadata(PacketReader reader, ItemPalette itemPalette,
|
||||
EntityMetadataPalette metadataPalette)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextMetadata(cache, itemPalette, metadataPalette));
|
||||
}
|
||||
|
||||
private static bool HasLpVec3Continuation(int firstByte) => (firstByte & 4) == 4;
|
||||
|
||||
private static double UnpackLpVec3(long packedAxis)
|
||||
|
|
@ -1109,6 +1348,27 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
);
|
||||
}
|
||||
|
||||
public (double X, double Y, double Z) ReadNextLpVec3Values(PacketReader reader)
|
||||
{
|
||||
int first = ReadNextByte(reader);
|
||||
if (first == 0)
|
||||
return (0.0, 0.0, 0.0);
|
||||
|
||||
int second = ReadNextByte(reader);
|
||||
uint high = (uint)ReadNextInt(reader);
|
||||
long packed = ((long)high << 16) | (long)(second << 8) | (uint)first;
|
||||
|
||||
long scale = first & 3;
|
||||
if (HasLpVec3Continuation(first))
|
||||
scale |= ((long)ReadNextVarInt(reader) & 0xFFFFFFFFL) << 2;
|
||||
|
||||
return (
|
||||
UnpackLpVec3(packed >> 3) * scale,
|
||||
UnpackLpVec3(packed >> 18) * scale,
|
||||
UnpackLpVec3(packed >> 33) * scale
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an LpVec3 (low-precision vec3) from the cache (1.21.9+) and discard it.
|
||||
/// </summary>
|
||||
|
|
@ -1117,6 +1377,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
ReadNextLpVec3Values(cache);
|
||||
}
|
||||
|
||||
public void ReadNextLpVec3(PacketReader reader)
|
||||
{
|
||||
ReadNextLpVec3Values(reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consume bytes for a ResolvableProfile (1.21.9+).
|
||||
/// Wire: Either(GameProfile, Partial) + PlayerSkin.Patch
|
||||
|
|
@ -1379,6 +1644,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public void ReadParticleData(PacketReader reader, ItemPalette itemPalette)
|
||||
{
|
||||
ReadWithQueueFallback(reader, cache => ReadParticleData(cache, itemPalette));
|
||||
}
|
||||
|
||||
private void ReadDustParticle(Queue<byte> cache)
|
||||
{
|
||||
ReadNextFloat(cache); // Red
|
||||
|
|
@ -1437,6 +1707,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand);
|
||||
}
|
||||
|
||||
public VillagerTrade ReadNextTrade(PacketReader reader, ItemPalette itemPalette)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, cache => ReadNextTrade(cache, itemPalette));
|
||||
}
|
||||
|
||||
public string ReadNextChat(Queue<byte> cache)
|
||||
{
|
||||
if (protocolversion >= Protocol18Handler.MC_1_20_4_Version)
|
||||
|
|
@ -1454,6 +1729,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public string ReadNextChat(PacketReader reader)
|
||||
{
|
||||
return ReadWithQueueFallback(reader, ReadNextChat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build an uncompressed Named Binary Tag blob for sending over the network
|
||||
/// </summary>
|
||||
|
|
@ -2035,5 +2315,22 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
return fields.ToArray();
|
||||
}
|
||||
|
||||
private static T ReadWithQueueFallback<T>(PacketReader reader, Func<Queue<byte>, T> read)
|
||||
{
|
||||
byte[] remaining = reader.CopyRemaining();
|
||||
Queue<byte> cache = new(remaining);
|
||||
T result = read(cache);
|
||||
reader.Skip(remaining.Length - cache.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ReadWithQueueFallback(PacketReader reader, Action<Queue<byte>> read)
|
||||
{
|
||||
byte[] remaining = reader.CopyRemaining();
|
||||
Queue<byte> cache = new(remaining);
|
||||
read(cache);
|
||||
reader.Skip(remaining.Length - cache.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||
{
|
||||
|
|
@ -59,7 +60,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
|
||||
public static bool IsCommandTreeAvailable => HasValidCommandTree();
|
||||
|
||||
public static void Read(DataTypes dataTypes, Queue<byte> packetData, int protocolVersion)
|
||||
public static void Read(DataTypes dataTypes, PacketReader packetData, int protocolVersion)
|
||||
{
|
||||
Reset();
|
||||
ConsoleIO.OnDeclareMinecraftCommand(Array.Empty<string>());
|
||||
|
|
@ -88,7 +89,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
: [];
|
||||
}
|
||||
|
||||
private static void ReadCommandTree(DataTypes dataTypes, Queue<byte> packetData, int protocolVersion)
|
||||
private static void ReadCommandTree(DataTypes dataTypes, PacketReader packetData, int protocolVersion)
|
||||
{
|
||||
int count = dataTypes.ReadNextVarInt(packetData);
|
||||
Nodes = new CommandNode[count];
|
||||
|
|
@ -117,7 +118,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
|
||||
private static CommandNode ReadArgumentNode(
|
||||
DataTypes dataTypes,
|
||||
Queue<byte> packetData,
|
||||
PacketReader packetData,
|
||||
int protocolVersion,
|
||||
byte flags,
|
||||
int[] children,
|
||||
|
|
@ -135,7 +136,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
return new(flags, children, redirectNode, name, descriptor, suggestionsType, parserId);
|
||||
}
|
||||
|
||||
private static int[] ReadChildIndices(DataTypes dataTypes, Queue<byte> packetData)
|
||||
private static int[] ReadChildIndices(DataTypes dataTypes, PacketReader packetData)
|
||||
{
|
||||
int childCount = dataTypes.ReadNextVarInt(packetData);
|
||||
int[] children = new int[childCount];
|
||||
|
|
@ -146,7 +147,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
return children;
|
||||
}
|
||||
|
||||
private static CommandArgumentDescriptor ReadArgumentDescriptor(DataTypes dataTypes, Queue<byte> packetData, ArgumentTypeLayout layout)
|
||||
private static CommandArgumentDescriptor ReadArgumentDescriptor(DataTypes dataTypes, PacketReader packetData, ArgumentTypeLayout layout)
|
||||
{
|
||||
switch (layout.PayloadKind)
|
||||
{
|
||||
|
|
@ -189,7 +190,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
}
|
||||
}
|
||||
|
||||
private static void ReadNumberBounds<TValue>(DataTypes dataTypes, Queue<byte> packetData, Func<DataTypes, Queue<byte>, TValue> readValue)
|
||||
private static void ReadNumberBounds<TValue>(DataTypes dataTypes, PacketReader packetData, Func<DataTypes, PacketReader, TValue> readValue)
|
||||
{
|
||||
byte flags = dataTypes.ReadNextByte(packetData);
|
||||
if ((flags & 0x01) != 0)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
|
|
@ -476,6 +477,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveAsync(byte[] buffer, int start, int offset, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (offset <= 0)
|
||||
return;
|
||||
|
||||
Stream stream = encrypted ? s! : c.GetStream();
|
||||
await stream.ReadExactlyAsync(buffer.AsMemory(start, offset), cancellationToken);
|
||||
}
|
||||
|
||||
private void Send(byte[] buffer)
|
||||
{
|
||||
if (encrypted)
|
||||
|
|
@ -484,7 +494,61 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
c.Client.Send(buffer);
|
||||
}
|
||||
|
||||
private bool Handshake(string uuid, string username, string sessionID, string host, int port, SessionToken session)
|
||||
private async Task SendAsync(byte[] buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (buffer.Length == 0)
|
||||
return;
|
||||
|
||||
Stream stream = encrypted ? s! : c.GetStream();
|
||||
await stream.WriteAsync(buffer.AsMemory(0, buffer.Length), cancellationToken);
|
||||
await stream.FlushAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<byte[]> ReadDataAsync(int offset, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (offset <= 0)
|
||||
return [];
|
||||
|
||||
byte[] cache = new byte[offset];
|
||||
await ReceiveAsync(cache, 0, offset, cancellationToken);
|
||||
return cache;
|
||||
}
|
||||
|
||||
private async Task<string> ReadNextStringAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
ushort length = (ushort)await ReadNextShortAsync(cancellationToken);
|
||||
if (length <= 0)
|
||||
return "";
|
||||
|
||||
byte[] cache = new byte[length * 2];
|
||||
await ReceiveAsync(cache, 0, length * 2, cancellationToken);
|
||||
return Encoding.BigEndianUnicode.GetString(cache);
|
||||
}
|
||||
|
||||
private async Task<byte[]> ReadNextByteArrayAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
short len = await ReadNextShortAsync(cancellationToken);
|
||||
byte[] data = new byte[len];
|
||||
await ReceiveAsync(data, 0, len, cancellationToken);
|
||||
return data;
|
||||
}
|
||||
|
||||
private async Task<short> ReadNextShortAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
byte[] tmp = new byte[2];
|
||||
await ReceiveAsync(tmp, 0, 2, cancellationToken);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToInt16(tmp, 0);
|
||||
}
|
||||
|
||||
private async Task<byte> ReadNextByteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
byte[] result = new byte[1];
|
||||
await ReceiveAsync(result, 0, 1, cancellationToken);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private async Task<bool> HandshakeAsync(string uuid, string username, string sessionID, string host, int port, SessionToken session, CancellationToken cancellationToken = default)
|
||||
{
|
||||
//array
|
||||
byte[] data = new byte[10 + (username.Length + host.Length) * 2];
|
||||
|
|
@ -518,28 +582,28 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
Array.Reverse(sh);
|
||||
sh.CopyTo(data, 6 + (username.Length * 2) + (host.Length * 2));
|
||||
|
||||
Send(data);
|
||||
await SendAsync(data, cancellationToken);
|
||||
|
||||
byte[] pid = new byte[1];
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
await ReceiveAsync(pid, 0, 1, cancellationToken);
|
||||
while (pid[0] == 0xFA) //Skip some early plugin messages
|
||||
{
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
ProcessPacket(pid[0]);
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
await ReceiveAsync(pid, 0, 1, cancellationToken);
|
||||
}
|
||||
if (pid[0] == 0xFD)
|
||||
{
|
||||
string serverID = ReadNextString();
|
||||
byte[] PublicServerkey = ReadNextByteArray();
|
||||
byte[] token = ReadNextByteArray();
|
||||
string serverID = await ReadNextStringAsync(cancellationToken);
|
||||
byte[] PublicServerkey = await ReadNextByteArrayAsync(cancellationToken);
|
||||
byte[] token = await ReadNextByteArrayAsync(cancellationToken);
|
||||
|
||||
if (serverID == "-")
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.mcc_server_offline, acceptnewlines: true);
|
||||
else if (Settings.Config.Logging.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_handshake, serverID));
|
||||
|
||||
return StartEncryption(uuid, username, sessionID, Config.Main.General.AccountType, token, serverID, PublicServerkey, session);
|
||||
return await StartEncryptionAsync(uuid, username, sessionID, Config.Main.General.AccountType, token, serverID, PublicServerkey, session, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -548,7 +612,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
private bool StartEncryption(string uuid, string username, string sessionID, LoginType type, byte[] token, string serverIDhash, byte[] serverPublicKey, SessionToken session)
|
||||
private async Task<bool> StartEncryptionAsync(string uuid, string username, string sessionID, LoginType type, byte[] token, string serverIDhash, byte[] serverPublicKey, SessionToken session, CancellationToken cancellationToken = default)
|
||||
{
|
||||
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!;
|
||||
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
|
||||
|
|
@ -571,7 +635,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
if (needCheckSession)
|
||||
{
|
||||
if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash, type))
|
||||
if (await ProtocolHandler.SessionCheckAsync(uuid, sessionID, serverHash, type))
|
||||
{
|
||||
session.ServerIDhash = serverIDhash;
|
||||
session.ServerPublicKey = serverPublicKey;
|
||||
|
|
@ -604,14 +668,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
token_enc.CopyTo(data, 5 + (short)key_enc.Length);
|
||||
|
||||
//Send it back
|
||||
Send(data);
|
||||
await SendAsync(data, cancellationToken);
|
||||
|
||||
//Getting the next packet
|
||||
byte[] pid = new byte[1];
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
await ReceiveAsync(pid, 0, 1, cancellationToken);
|
||||
if (pid[0] == 0xFC)
|
||||
{
|
||||
ReadData(4);
|
||||
await ReadDataAsync(4, cancellationToken);
|
||||
s = new AesCfb8Stream(c.GetStream(), secretKey);
|
||||
encrypted = true;
|
||||
return true;
|
||||
|
|
@ -625,9 +689,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
|
||||
{
|
||||
if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session))
|
||||
return LoginAsync(playerKeyPair, session, isTransfer).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private async Task<bool> LoginAsync(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
|
||||
{
|
||||
if (await HandshakeAsync(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session))
|
||||
{
|
||||
Send(new byte[] { 0xCD, 0 });
|
||||
await SendAsync([0xCD, 0]);
|
||||
try
|
||||
{
|
||||
byte[] pid = new byte[1];
|
||||
|
|
@ -635,22 +704,24 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
if (c.Connected)
|
||||
{
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
await ReceiveAsync(pid, 0, 1);
|
||||
while (pid[0] >= 0xC0 && pid[0] != 0xFF) //Skip some early packets or plugin messages
|
||||
{
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
ProcessPacket(pid[0]);
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
await ReceiveAsync(pid, 0, 1);
|
||||
}
|
||||
if (pid[0] == (byte)1)
|
||||
{
|
||||
ReadData(4); ReadNextString(); ReadData(5);
|
||||
await ReadDataAsync(4);
|
||||
_ = await ReadNextStringAsync();
|
||||
await ReadDataAsync(5);
|
||||
StartUpdating();
|
||||
return true; //The Server accepted the request
|
||||
}
|
||||
else if (pid[0] == (byte)0xFF)
|
||||
{
|
||||
string reason = ReadNextString();
|
||||
string reason = await ReadNextStringAsync();
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, reason);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SoundEvent> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
using MinecraftClient.Scripting;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
|
|
@ -52,7 +53,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
(int packetID, Queue<byte> packetData) = await protocol18.ReadNextPacketAsync(cancellationToken);
|
||||
IncomingPacket packet = await protocol18.ReadNextPacketAsync(cancellationToken);
|
||||
int packetID = packet.PacketId;
|
||||
PacketReader packetData = packet.CreateReader();
|
||||
|
||||
if (packetID == 0x40) // Disconnect
|
||||
{
|
||||
|
|
@ -93,7 +96,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="packetData">Packet data to read from</param>
|
||||
/// <returns>Length from packet data</returns>
|
||||
public int ReadNextVarShort(Queue<byte> packetData)
|
||||
public int ReadNextVarShort(PacketReader packetData)
|
||||
{
|
||||
if (ForgeEnabled())
|
||||
{
|
||||
|
|
@ -114,7 +117,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="packetData">Plugin message data</param>
|
||||
/// <param name="currentDimension">Current world dimension</param>
|
||||
/// <returns>TRUE if the plugin message was recognized and handled</returns>
|
||||
public bool HandlePluginMessage(string channel, Queue<byte> packetData, ref int currentDimension)
|
||||
public bool HandlePluginMessage(string channel, PacketReader packetData, ref int currentDimension)
|
||||
{
|
||||
if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML && fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
|
|
@ -243,7 +246,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="packetData">Plugin message data</param>
|
||||
/// <param name="responseData">Response data to return to server</param>
|
||||
/// <returns>TRUE/FALSE depending on whether the packet was understood or not</returns>
|
||||
public bool HandleLoginPluginRequest(string channel, Queue<byte> packetData, ref List<byte> responseData)
|
||||
public bool HandleLoginPluginRequest(string channel, PacketReader packetData, ref List<byte> responseData)
|
||||
{
|
||||
if (ForgeEnabled() && (forgeInfo!.Version == FMLVersion.FML2 || forgeInfo!.Version == FMLVersion.FML3) && channel == "fml:loginwrapper")
|
||||
{
|
||||
|
|
@ -332,7 +335,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
// FML3 specific,
|
||||
List<string> dataPackRegistries = new();
|
||||
if (forgeInfo!.Version == FMLVersion.FML3 && packetData.Count != 0)
|
||||
if (forgeInfo!.Version == FMLVersion.FML3 && packetData.RemainingLength != 0)
|
||||
{
|
||||
int dataPackRegistryCount = dataTypes.ReadNextVarInt(packetData);
|
||||
for (int i = 0; i < dataPackRegistryCount; i++)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
|||
//using System.Linq;
|
||||
//using System.Text;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
|
|
@ -23,7 +24,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="cache">Cache for reading data</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private Chunk? ReadBlockStatesField(Queue<byte> cache)
|
||||
private Chunk? ReadBlockStatesField(PacketReader cache)
|
||||
{
|
||||
// read Block states (Type: Paletted Container)
|
||||
byte bitsPerEntry = dataTypes.ReadNextByte(cache);
|
||||
|
|
@ -134,7 +135,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="cache">Cache for reading chunk data</param>
|
||||
/// <param name="cancellationToken">token to cancel the task</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ulong[]? verticalStripBitmask, Queue<byte> cache)
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ulong[]? verticalStripBitmask, PacketReader cache)
|
||||
{
|
||||
World world = handler.GetWorld();
|
||||
|
||||
|
|
@ -236,7 +237,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="cache">Cache for reading chunk data</param>
|
||||
/// <param name="cancellationToken">token to cancel the task</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, ushort chunkMask2, bool hasSkyLight, bool chunksContinuous, int currentDimension, Queue<byte> cache)
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, ushort chunkMask2, bool hasSkyLight, bool chunksContinuous, int currentDimension, PacketReader cache)
|
||||
{
|
||||
World world = handler.GetWorld();
|
||||
|
||||
|
|
|
|||
|
|
@ -107,24 +107,24 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
internal Tuple<int, Queue<byte>> GetNextPacket(int compressionThreshold, DataTypes dataTypes)
|
||||
internal IncomingPacket GetNextPacket(int compressionThreshold, DataTypes dataTypes)
|
||||
{
|
||||
int packetLength = ReadNextVarIntRaw();
|
||||
using PacketReadStream packetStream = new(readStream, packetLength);
|
||||
byte[] payload = ReadPacketPayload(packetStream, compressionThreshold);
|
||||
Queue<byte> packetData = new(payload);
|
||||
var packetData = new PacketReader(payload);
|
||||
int packetId = dataTypes.ReadNextVarInt(packetData);
|
||||
return new(packetId, packetData);
|
||||
return new(packetId, packetData.CopyRemaining());
|
||||
}
|
||||
|
||||
internal async Task<Tuple<int, Queue<byte>>> GetNextPacketAsync(int compressionThreshold, DataTypes dataTypes, CancellationToken cancellationToken)
|
||||
internal async Task<IncomingPacket> GetNextPacketAsync(int compressionThreshold, DataTypes dataTypes, CancellationToken cancellationToken)
|
||||
{
|
||||
int packetLength = await ReadNextVarIntRawAsync(cancellationToken);
|
||||
await using PacketReadStream packetStream = new(readStream, packetLength);
|
||||
byte[] payload = await ReadPacketPayloadAsync(packetStream, compressionThreshold, cancellationToken);
|
||||
Queue<byte> packetData = new(payload);
|
||||
var packetData = new PacketReader(payload);
|
||||
int packetId = dataTypes.ReadNextVarInt(packetData);
|
||||
return new(packetId, packetData);
|
||||
return new(packetId, packetData.CopyRemaining());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
13
MinecraftClient/Protocol/PacketPipeline/IncomingPacket.cs
Normal file
13
MinecraftClient/Protocol/PacketPipeline/IncomingPacket.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
internal readonly record struct IncomingPacket(int PacketId, byte[] Payload)
|
||||
{
|
||||
public PacketReader CreateReader()
|
||||
{
|
||||
return new PacketReader(Payload);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> PayloadSpan => Payload;
|
||||
}
|
||||
123
MinecraftClient/Protocol/PacketPipeline/PacketReader.cs
Normal file
123
MinecraftClient/Protocol/PacketPipeline/PacketReader.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
public sealed class PacketReader
|
||||
{
|
||||
private readonly byte[] buffer;
|
||||
private int position;
|
||||
|
||||
public PacketReader(byte[] buffer)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(buffer);
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public int Position => position;
|
||||
public int RemainingLength => buffer.Length - position;
|
||||
public ReadOnlySpan<byte> RemainingSpan => buffer.AsSpan(position);
|
||||
public ReadOnlySpan<byte> FullSpan => buffer;
|
||||
|
||||
public byte[] GetRawData() => buffer;
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
EnsureAvailable(1);
|
||||
return buffer[position++];
|
||||
}
|
||||
|
||||
public byte[] ReadData(int length)
|
||||
{
|
||||
if (length == 0)
|
||||
return [];
|
||||
|
||||
EnsureAvailable(length);
|
||||
byte[] data = GC.AllocateUninitializedArray<byte>(length);
|
||||
Buffer.BlockCopy(buffer, position, data, 0, length);
|
||||
position += length;
|
||||
return data;
|
||||
}
|
||||
|
||||
public void ReadData(Span<byte> destination)
|
||||
{
|
||||
if (destination.Length == 0)
|
||||
return;
|
||||
|
||||
EnsureAvailable(destination.Length);
|
||||
buffer.AsSpan(position, destination.Length).CopyTo(destination);
|
||||
position += destination.Length;
|
||||
}
|
||||
|
||||
public void ReadDataReverse(Span<byte> destination)
|
||||
{
|
||||
if (destination.Length == 0)
|
||||
return;
|
||||
|
||||
EnsureAvailable(destination.Length);
|
||||
for (int i = destination.Length - 1; i >= 0; --i)
|
||||
destination[i] = buffer[position++];
|
||||
}
|
||||
|
||||
public void Skip(int length)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
EnsureAvailable(length);
|
||||
position += length;
|
||||
}
|
||||
|
||||
public ushort ReadUInt16BigEndian()
|
||||
{
|
||||
EnsureAvailable(sizeof(ushort));
|
||||
ushort value = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(position, sizeof(ushort)));
|
||||
position += sizeof(ushort);
|
||||
return value;
|
||||
}
|
||||
|
||||
public short ReadInt16BigEndian()
|
||||
{
|
||||
EnsureAvailable(sizeof(short));
|
||||
short value = BinaryPrimitives.ReadInt16BigEndian(buffer.AsSpan(position, sizeof(short)));
|
||||
position += sizeof(short);
|
||||
return value;
|
||||
}
|
||||
|
||||
public int ReadInt32BigEndian()
|
||||
{
|
||||
EnsureAvailable(sizeof(int));
|
||||
int value = BinaryPrimitives.ReadInt32BigEndian(buffer.AsSpan(position, sizeof(int)));
|
||||
position += sizeof(int);
|
||||
return value;
|
||||
}
|
||||
|
||||
public long ReadInt64BigEndian()
|
||||
{
|
||||
EnsureAvailable(sizeof(long));
|
||||
long value = BinaryPrimitives.ReadInt64BigEndian(buffer.AsSpan(position, sizeof(long)));
|
||||
position += sizeof(long);
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte[] CopyRemaining()
|
||||
{
|
||||
return ReadOnlySpanToArray(RemainingSpan);
|
||||
}
|
||||
|
||||
private void EnsureAvailable(int length)
|
||||
{
|
||||
if (RemainingLength < length)
|
||||
throw new OverflowException("Reached the end of the packet.");
|
||||
}
|
||||
|
||||
private static byte[] ReadOnlySpanToArray(ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
return [];
|
||||
|
||||
byte[] copy = GC.AllocateUninitializedArray<byte>(span.Length);
|
||||
span.CopyTo(copy);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue