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();
|
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)
|
private static async Task<bool> WaitForContainerOpenAsync(McClient client, ISet<int> beforeIds, int waitMs, Action<ContainerOpenState> onOpened)
|
||||||
{
|
{
|
||||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
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)
|
private static async Task<bool> WaitForContainerCloseAsync(McClient client, int inventoryId, int waitMs)
|
||||||
{
|
{
|
||||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||||
|
|
@ -2453,14 +2409,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
||||||
: 0;
|
: 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)
|
private static int CountItemInRange(Container inventory, ItemType itemType, int startSlot, int endSlot)
|
||||||
{
|
{
|
||||||
return inventory.Items
|
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)
|
private static List<NearbyPlayerSnapshot> BuildTrackedPlayerSnapshots(McClient client, bool includeSelf)
|
||||||
{
|
{
|
||||||
Location playerLocation = client.GetCurrentLocation();
|
Location playerLocation = client.GetCurrentLocation();
|
||||||
|
|
@ -3121,25 +2960,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
||||||
return new string(buffer);
|
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)
|
private static async Task<(bool Arrived, Location FinalLocation)> WaitForArrivalAsync(McClient client, Location goal, int waitMs, double tolerance)
|
||||||
{
|
{
|
||||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||||
|
|
@ -3186,24 +3006,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
||||||
return Math.Clamp(timeoutMs, MinPathQueryTimeoutMs, MaxPathQueryTimeoutMs);
|
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)
|
private static async Task<(bool Changed, Block AfterBlock)> WaitForBlockChangeAsync(McClient client, Location target, Block beforeBlock, int waitMs)
|
||||||
{
|
{
|
||||||
Block afterBlock = beforeBlock;
|
Block afterBlock = beforeBlock;
|
||||||
|
|
@ -3337,22 +3139,6 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
||||||
.ToArray();
|
.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)
|
private static int GetInventoryItemCount(McClient client, ItemType itemType)
|
||||||
{
|
{
|
||||||
Container? inventory = client.GetInventory(0);
|
Container? inventory = client.GetInventory(0);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using MinecraftClient.Inventory;
|
||||||
using MinecraftClient.Inventory.ItemPalettes;
|
using MinecraftClient.Inventory.ItemPalettes;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
using MinecraftClient.Mapping.EntityPalettes;
|
using MinecraftClient.Mapping.EntityPalettes;
|
||||||
|
using MinecraftClient.Protocol.PacketPipeline;
|
||||||
using MinecraftClient.Protocol.Handlers.StructuredComponents;
|
using MinecraftClient.Protocol.Handlers.StructuredComponents;
|
||||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||||
using MinecraftClient.Protocol.Message;
|
using MinecraftClient.Protocol.Message;
|
||||||
|
|
@ -44,6 +45,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public byte[] ReadData(int offset, PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadData(offset);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read some data from a cache of bytes and remove it from the cache
|
/// Read some data from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -56,6 +63,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
dest[i] = cache.Dequeue();
|
dest[i] = cache.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void ReadDataReverse(PacketReader reader, Span<byte> dest)
|
||||||
|
{
|
||||||
|
reader.ReadDataReverse(dest);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove some data from the cache
|
/// Remove some data from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -68,6 +81,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
cache.Dequeue();
|
cache.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void DropData(int offset, PacketReader reader)
|
||||||
|
{
|
||||||
|
reader.Skip(offset);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a string from a cache of bytes and remove it from the cache
|
/// Read a string from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -84,6 +103,13 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
else return "";
|
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>
|
/// <summary>
|
||||||
/// Skip a string from a cache of bytes and remove it from the cache
|
/// Skip a string from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -94,6 +120,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
DropData(length, cache);
|
DropData(length, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SkipNextString(PacketReader reader)
|
||||||
|
{
|
||||||
|
int length = ReadNextVarInt(reader);
|
||||||
|
DropData(length, reader);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a boolean from a cache of bytes and remove it from the cache
|
/// Read a boolean from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -104,6 +136,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ReadNextByte(cache) != 0x00;
|
return ReadNextByte(cache) != 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public bool ReadNextBool(PacketReader reader)
|
||||||
|
{
|
||||||
|
return ReadNextByte(reader) != 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a short integer from a cache of bytes and remove it from the cache
|
/// Read a short integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -117,6 +155,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToInt16(rawValue);
|
return BitConverter.ToInt16(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public short ReadNextShort(PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadInt16BigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an integer from a cache of bytes and remove it from the cache
|
/// Read an integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -130,6 +174,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToInt32(rawValue);
|
return BitConverter.ToInt32(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public int ReadNextInt(PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadInt32BigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a long integer from a cache of bytes and remove it from the cache
|
/// Read a long integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -143,6 +193,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToInt64(rawValue);
|
return BitConverter.ToInt64(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public long ReadNextLong(PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadInt64BigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -156,6 +212,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToUInt16(rawValue);
|
return BitConverter.ToUInt16(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public ushort ReadNextUShort(PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadUInt16BigEndian();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned long integer from a cache of bytes and remove it from the cache
|
/// Read an unsigned long integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -169,6 +231,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToUInt64(rawValue);
|
return BitConverter.ToUInt64(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public ulong ReadNextULong(PacketReader reader)
|
||||||
|
{
|
||||||
|
return unchecked((ulong)reader.ReadInt64BigEndian());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a Location encoded as an ulong field and remove it from the cache
|
/// Read a Location encoded as an ulong field and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -199,6 +267,32 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return new Location(x, y, z);
|
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>
|
/// <summary>
|
||||||
/// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache
|
/// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -212,6 +306,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
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>
|
/// <summary>
|
||||||
/// Read a uuid from a cache of bytes and remove it from the cache
|
/// Read a uuid from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -228,6 +331,16 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return guid;
|
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>
|
/// <summary>
|
||||||
/// Read a byte array from a cache of bytes and remove it from the cache
|
/// Read a byte array from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -242,6 +355,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ReadData(len, cache);
|
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>
|
/// <summary>
|
||||||
/// Read a byte array with given length from a cache of bytes and remove it from the cache
|
/// Read a byte array with given length from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -254,6 +376,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ReadData(length, cache);
|
return ReadData(length, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public byte[] ReadNextByteArray(PacketReader reader, int length)
|
||||||
|
{
|
||||||
|
return ReadData(length, reader);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a length-prefixed array of unsigned long integers and removes it from the cache
|
/// Reads a length-prefixed array of unsigned long integers and removes it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -268,6 +396,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
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>
|
/// <summary>
|
||||||
/// Read a double from a cache of bytes and remove it from the cache
|
/// Read a double from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -281,6 +418,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToDouble(rawValue);
|
return BitConverter.ToDouble(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public double ReadNextDouble(PacketReader reader)
|
||||||
|
{
|
||||||
|
return BitConverter.Int64BitsToDouble(ReadNextLong(reader));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a float from a cache of bytes and remove it from the cache
|
/// Read a float from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -294,6 +437,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return BitConverter.ToSingle(rawValue);
|
return BitConverter.ToSingle(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public float ReadNextFloat(PacketReader reader)
|
||||||
|
{
|
||||||
|
return BitConverter.Int32BitsToSingle(ReadNextInt(reader));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an integer from the network
|
/// Read an integer from the network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -357,6 +506,22 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return i;
|
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>
|
/// <summary>
|
||||||
/// Skip a VarInt from a cache of bytes with better performance
|
/// Skip a VarInt from a cache of bytes with better performance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -369,6 +534,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void SkipNextVarInt(PacketReader reader)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
if ((ReadNextByte(reader) & 0x80) != 128)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an "extended short", which is actually an int of some kind, from the cache of bytes.
|
/// 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
|
/// 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;
|
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>
|
/// <summary>
|
||||||
/// Read a long from a cache of bytes and remove it from the cache
|
/// Read a long from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -415,6 +601,27 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
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>
|
/// <summary>
|
||||||
/// Read a single byte from a cache of bytes and remove it from the cache
|
/// Read a single byte from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -426,6 +633,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public byte ReadNextByte(PacketReader reader)
|
||||||
|
{
|
||||||
|
return reader.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an uncompressed Named Binary Tag blob and remove it from the cache
|
/// Read an uncompressed Named Binary Tag blob and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -434,6 +647,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ReadNextNbt(cache, true);
|
return ReadNextNbt(cache, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, object> ReadNextNbt(PacketReader reader)
|
||||||
|
{
|
||||||
|
return ReadWithQueueFallback(reader, cache => ReadNextNbt(cache, true));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an ItemStackTemplate (26.1+) from a cache of bytes.
|
/// Read an ItemStackTemplate (26.1+) from a cache of bytes.
|
||||||
/// Unlike ItemStack, this uses item-first encoding: item_id, count, DataComponentPatch.
|
/// Unlike ItemStack, this uses item-first encoding: item_id, count, DataComponentPatch.
|
||||||
|
|
@ -465,6 +683,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Item ReadNextItemStackTemplate(PacketReader reader, ItemPalette itemPalette)
|
||||||
|
{
|
||||||
|
return ReadWithQueueFallback(reader, cache => ReadNextItemStackTemplate(cache, itemPalette));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a single item slot from a cache of bytes and remove it from the cache
|
/// Read a single item slot from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </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)
|
private void ReadNextDetail(Queue<byte> cache)
|
||||||
{
|
{
|
||||||
var potionEffectId = ReadNextVarInt(cache);
|
var potionEffectId = ReadNextVarInt(cache);
|
||||||
|
|
@ -724,6 +952,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Entity ReadNextEntity(PacketReader reader, EntityPalette entityPalette, bool living)
|
||||||
|
{
|
||||||
|
return ReadWithQueueFallback(reader, cache => ReadNextEntity(cache, entityPalette, living));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
|
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
|
||||||
/// </summary>
|
/// </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 bool HasLpVec3Continuation(int firstByte) => (firstByte & 4) == 4;
|
||||||
|
|
||||||
private static double UnpackLpVec3(long packedAxis)
|
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>
|
/// <summary>
|
||||||
/// Read an LpVec3 (low-precision vec3) from the cache (1.21.9+) and discard it.
|
/// Read an LpVec3 (low-precision vec3) from the cache (1.21.9+) and discard it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1117,6 +1377,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
ReadNextLpVec3Values(cache);
|
ReadNextLpVec3Values(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReadNextLpVec3(PacketReader reader)
|
||||||
|
{
|
||||||
|
ReadNextLpVec3Values(reader);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Consume bytes for a ResolvableProfile (1.21.9+).
|
/// Consume bytes for a ResolvableProfile (1.21.9+).
|
||||||
/// Wire: Either(GameProfile, Partial) + PlayerSkin.Patch
|
/// 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)
|
private void ReadDustParticle(Queue<byte> cache)
|
||||||
{
|
{
|
||||||
ReadNextFloat(cache); // Red
|
ReadNextFloat(cache); // Red
|
||||||
|
|
@ -1437,6 +1707,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand);
|
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)
|
public string ReadNextChat(Queue<byte> cache)
|
||||||
{
|
{
|
||||||
if (protocolversion >= Protocol18Handler.MC_1_20_4_Version)
|
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>
|
/// <summary>
|
||||||
/// Build an uncompressed Named Binary Tag blob for sending over the network
|
/// Build an uncompressed Named Binary Tag blob for sending over the network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2035,5 +2315,22 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
return fields.ToArray();
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using MinecraftClient.Protocol.PacketPipeline;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||||
{
|
{
|
||||||
|
|
@ -59,7 +60,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||||
|
|
||||||
public static bool IsCommandTreeAvailable => HasValidCommandTree();
|
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();
|
Reset();
|
||||||
ConsoleIO.OnDeclareMinecraftCommand(Array.Empty<string>());
|
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);
|
int count = dataTypes.ReadNextVarInt(packetData);
|
||||||
Nodes = new CommandNode[count];
|
Nodes = new CommandNode[count];
|
||||||
|
|
@ -117,7 +118,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||||
|
|
||||||
private static CommandNode ReadArgumentNode(
|
private static CommandNode ReadArgumentNode(
|
||||||
DataTypes dataTypes,
|
DataTypes dataTypes,
|
||||||
Queue<byte> packetData,
|
PacketReader packetData,
|
||||||
int protocolVersion,
|
int protocolVersion,
|
||||||
byte flags,
|
byte flags,
|
||||||
int[] children,
|
int[] children,
|
||||||
|
|
@ -135,7 +136,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||||
return new(flags, children, redirectNode, name, descriptor, suggestionsType, parserId);
|
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 childCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
int[] children = new int[childCount];
|
int[] children = new int[childCount];
|
||||||
|
|
@ -146,7 +147,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||||
return children;
|
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)
|
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);
|
byte flags = dataTypes.ReadNextByte(packetData);
|
||||||
if ((flags & 0x01) != 0)
|
if ((flags & 0x01) != 0)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Cryptography;
|
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)
|
private void Send(byte[] buffer)
|
||||||
{
|
{
|
||||||
if (encrypted)
|
if (encrypted)
|
||||||
|
|
@ -484,7 +494,61 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
c.Client.Send(buffer);
|
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
|
//array
|
||||||
byte[] data = new byte[10 + (username.Length + host.Length) * 2];
|
byte[] data = new byte[10 + (username.Length + host.Length) * 2];
|
||||||
|
|
@ -518,28 +582,28 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
Array.Reverse(sh);
|
Array.Reverse(sh);
|
||||||
sh.CopyTo(data, 6 + (username.Length * 2) + (host.Length * 2));
|
sh.CopyTo(data, 6 + (username.Length * 2) + (host.Length * 2));
|
||||||
|
|
||||||
Send(data);
|
await SendAsync(data, cancellationToken);
|
||||||
|
|
||||||
byte[] pid = new byte[1];
|
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
|
while (pid[0] == 0xFA) //Skip some early plugin messages
|
||||||
{
|
{
|
||||||
using (MainThreadExecutionScope.Enter(handler))
|
using (MainThreadExecutionScope.Enter(handler))
|
||||||
ProcessPacket(pid[0]);
|
ProcessPacket(pid[0]);
|
||||||
Receive(pid, 0, 1, SocketFlags.None);
|
await ReceiveAsync(pid, 0, 1, cancellationToken);
|
||||||
}
|
}
|
||||||
if (pid[0] == 0xFD)
|
if (pid[0] == 0xFD)
|
||||||
{
|
{
|
||||||
string serverID = ReadNextString();
|
string serverID = await ReadNextStringAsync(cancellationToken);
|
||||||
byte[] PublicServerkey = ReadNextByteArray();
|
byte[] PublicServerkey = await ReadNextByteArrayAsync(cancellationToken);
|
||||||
byte[] token = ReadNextByteArray();
|
byte[] token = await ReadNextByteArrayAsync(cancellationToken);
|
||||||
|
|
||||||
if (serverID == "-")
|
if (serverID == "-")
|
||||||
ConsoleIO.WriteLineFormatted("§8" + Translations.mcc_server_offline, acceptnewlines: true);
|
ConsoleIO.WriteLineFormatted("§8" + Translations.mcc_server_offline, acceptnewlines: true);
|
||||||
else if (Settings.Config.Logging.DebugMessages)
|
else if (Settings.Config.Logging.DebugMessages)
|
||||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_handshake, serverID));
|
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
|
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)!;
|
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!;
|
||||||
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
|
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
|
||||||
|
|
@ -571,7 +635,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
if (needCheckSession)
|
if (needCheckSession)
|
||||||
{
|
{
|
||||||
if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash, type))
|
if (await ProtocolHandler.SessionCheckAsync(uuid, sessionID, serverHash, type))
|
||||||
{
|
{
|
||||||
session.ServerIDhash = serverIDhash;
|
session.ServerIDhash = serverIDhash;
|
||||||
session.ServerPublicKey = serverPublicKey;
|
session.ServerPublicKey = serverPublicKey;
|
||||||
|
|
@ -604,14 +668,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
token_enc.CopyTo(data, 5 + (short)key_enc.Length);
|
token_enc.CopyTo(data, 5 + (short)key_enc.Length);
|
||||||
|
|
||||||
//Send it back
|
//Send it back
|
||||||
Send(data);
|
await SendAsync(data, cancellationToken);
|
||||||
|
|
||||||
//Getting the next packet
|
//Getting the next packet
|
||||||
byte[] pid = new byte[1];
|
byte[] pid = new byte[1];
|
||||||
Receive(pid, 0, 1, SocketFlags.None);
|
await ReceiveAsync(pid, 0, 1, cancellationToken);
|
||||||
if (pid[0] == 0xFC)
|
if (pid[0] == 0xFC)
|
||||||
{
|
{
|
||||||
ReadData(4);
|
await ReadDataAsync(4, cancellationToken);
|
||||||
s = new AesCfb8Stream(c.GetStream(), secretKey);
|
s = new AesCfb8Stream(c.GetStream(), secretKey);
|
||||||
encrypted = true;
|
encrypted = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -625,9 +689,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
|
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
|
try
|
||||||
{
|
{
|
||||||
byte[] pid = new byte[1];
|
byte[] pid = new byte[1];
|
||||||
|
|
@ -635,22 +704,24 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
if (c.Connected)
|
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
|
while (pid[0] >= 0xC0 && pid[0] != 0xFF) //Skip some early packets or plugin messages
|
||||||
{
|
{
|
||||||
using (MainThreadExecutionScope.Enter(handler))
|
using (MainThreadExecutionScope.Enter(handler))
|
||||||
ProcessPacket(pid[0]);
|
ProcessPacket(pid[0]);
|
||||||
Receive(pid, 0, 1, SocketFlags.None);
|
await ReceiveAsync(pid, 0, 1);
|
||||||
}
|
}
|
||||||
if (pid[0] == (byte)1)
|
if (pid[0] == (byte)1)
|
||||||
{
|
{
|
||||||
ReadData(4); ReadNextString(); ReadData(5);
|
await ReadDataAsync(4);
|
||||||
|
_ = await ReadNextStringAsync();
|
||||||
|
await ReadDataAsync(5);
|
||||||
StartUpdating();
|
StartUpdating();
|
||||||
return true; //The Server accepted the request
|
return true; //The Server accepted the request
|
||||||
}
|
}
|
||||||
else if (pid[0] == (byte)0xFF)
|
else if (pid[0] == (byte)0xFF)
|
||||||
{
|
{
|
||||||
string reason = ReadNextString();
|
string reason = await ReadNextStringAsync();
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, reason);
|
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, reason);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ using MinecraftClient.Protocol.Handlers.Forge;
|
||||||
using MinecraftClient.Protocol.Handlers.packet.s2c;
|
using MinecraftClient.Protocol.Handlers.packet.s2c;
|
||||||
using MinecraftClient.Protocol.Handlers.PacketPalettes;
|
using MinecraftClient.Protocol.Handlers.PacketPalettes;
|
||||||
using MinecraftClient.Protocol.Message;
|
using MinecraftClient.Protocol.Message;
|
||||||
|
using MinecraftClient.Protocol.PacketPipeline;
|
||||||
using MinecraftClient.Protocol.ProfileKey;
|
using MinecraftClient.Protocol.ProfileKey;
|
||||||
using MinecraftClient.Protocol.Session;
|
using MinecraftClient.Protocol.Session;
|
||||||
using MinecraftClient.Proxy;
|
using MinecraftClient.Proxy;
|
||||||
|
|
@ -91,7 +92,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
private readonly int rawProtocolVersion;
|
private readonly int rawProtocolVersion;
|
||||||
private int currentDimension;
|
private int currentDimension;
|
||||||
private bool isOnlineMode = false;
|
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 readonly Dictionary<string, bool> legacyAchievementProgress = new(StringComparer.Ordinal);
|
||||||
private float LastYaw, LastPitch;
|
private float LastYaw, LastPitch;
|
||||||
private double lastSentX, lastSentY, lastSentZ;
|
private double lastSentX, lastSentY, lastSentZ;
|
||||||
|
|
@ -309,8 +310,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
if (packetQueue.TryTake(out var packetInfo, 1))
|
if (packetQueue.TryTake(out var packetInfo, 1))
|
||||||
{
|
{
|
||||||
var (packetId, packetData) = packetInfo;
|
HandlePacket(packetInfo.PacketId, packetInfo.CreateReader());
|
||||||
HandlePacket(packetId, packetData);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,27 +393,27 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="packetId">will contain packet ID</param>
|
/// <param name="packetId">will contain packet ID</param>
|
||||||
/// <param name="packetData">will contain raw packet Data</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,
|
protocolVersion >= MC_1_8_Version ? compression_treshold : -1,
|
||||||
dataTypes);
|
dataTypes);
|
||||||
if (handler.GetNetworkPacketCaptureEnabled())
|
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,
|
protocolVersion >= MC_1_8_Version ? compression_treshold : -1,
|
||||||
dataTypes,
|
dataTypes,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
if (handler.GetNetworkPacketCaptureEnabled())
|
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>
|
/// <summary>
|
||||||
|
|
@ -422,11 +422,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="packetId">Packet ID</param>
|
/// <param name="packetId">Packet ID</param>
|
||||||
/// <param name="packetData">Packet contents</param>
|
/// <param name="packetData">Packet contents</param>
|
||||||
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
|
/// <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,
|
byte[] _copy = packetData.GetRawData();
|
||||||
// the packetData queue will have been processed and the data will be lost
|
|
||||||
var _copy = packetData.ToArray();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -488,11 +486,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigurationPacketTypesIn.KeepAlive:
|
case ConfigurationPacketTypesIn.KeepAlive:
|
||||||
SendPacket(ConfigurationPacketTypesOut.KeepAlive, packetData);
|
SendPacket(ConfigurationPacketTypesOut.KeepAlive, packetData.CopyRemaining());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigurationPacketTypesIn.Ping:
|
case ConfigurationPacketTypesIn.Ping:
|
||||||
SendPacket(ConfigurationPacketTypesOut.Pong, packetData);
|
SendPacket(ConfigurationPacketTypesOut.Pong, packetData.CopyRemaining());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigurationPacketTypesIn.RegistryData:
|
case ConfigurationPacketTypesIn.RegistryData:
|
||||||
|
|
@ -674,7 +672,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleResourcePackPacket(Queue<byte> packetData)
|
public void HandleResourcePackPacket(PacketReader packetData)
|
||||||
{
|
{
|
||||||
var uuid = Guid.Empty;
|
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))
|
switch (packetPalette.GetIncomingTypeById(packetId))
|
||||||
{
|
{
|
||||||
case PacketTypesIn.KeepAlive: // Keep Alive (Play)
|
case PacketTypesIn.KeepAlive: // Keep Alive (Play)
|
||||||
SendPacket(PacketTypesOut.KeepAlive, packetData);
|
SendPacket(PacketTypesOut.KeepAlive, packetData.CopyRemaining());
|
||||||
handler.OnServerKeepAlive();
|
handler.OnServerKeepAlive();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PacketTypesIn.Ping:
|
case PacketTypesIn.Ping:
|
||||||
SendPacket(PacketTypesOut.Pong, packetData);
|
SendPacket(PacketTypesOut.Pong, packetData.CopyRemaining());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PacketTypesIn.JoinGame:
|
case PacketTypesIn.JoinGame:
|
||||||
|
|
@ -1602,7 +1600,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap,
|
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap,
|
||||||
currentDimension == 0, chunksContinuous, currentDimension,
|
currentDimension == 0, chunksContinuous, currentDimension,
|
||||||
new Queue<byte>(decompressed));
|
new PacketReader(decompressed));
|
||||||
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -1984,7 +1982,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
hasSkyLight = dataTypes.ReadNextBool(packetData);
|
hasSkyLight = dataTypes.ReadNextBool(packetData);
|
||||||
var compressed = dataTypes.ReadData(compressedDataSize, packetData);
|
var compressed = dataTypes.ReadData(compressedDataSize, packetData);
|
||||||
var decompressed = ZlibUtils.Decompress(compressed);
|
var decompressed = ZlibUtils.Decompress(compressed);
|
||||||
chunkData = new Queue<byte>(decompressed);
|
chunkData = new PacketReader(decompressed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -2297,7 +2295,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
// Length is unneeded as the whole remaining packetData is the entire payload of the packet.
|
// Length is unneeded as the whole remaining packetData is the entire payload of the packet.
|
||||||
if (protocolVersion < MC_1_8_Version)
|
if (protocolVersion < MC_1_8_Version)
|
||||||
pForge.ReadNextVarShort(packetData);
|
pForge.ReadNextVarShort(packetData);
|
||||||
handler.OnPluginChannelMessage(channel, packetData.ToArray());
|
handler.OnPluginChannelMessage(channel, packetData.CopyRemaining());
|
||||||
return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
|
return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
|
||||||
case PacketTypesIn.Disconnect:
|
case PacketTypesIn.Disconnect:
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick,
|
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.
|
/// Read a Holder<SoundEvent> from packet data and return its key when inline.
|
||||||
/// Returns null when the holder is a registry reference.
|
/// Returns null when the holder is a registry reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string? ReadSoundEventHolderName(Queue<byte> packetData)
|
private string? ReadSoundEventHolderName(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int soundHolderId = dataTypes.ReadNextVarInt(packetData);
|
int soundHolderId = dataTypes.ReadNextVarInt(packetData);
|
||||||
if (soundHolderId != 0)
|
if (soundHolderId != 0)
|
||||||
|
|
@ -3339,7 +3337,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the Statistics packet for pre-1.12 legacy achievements.
|
/// Handle the Statistics packet for pre-1.12 legacy achievements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleLegacyStatistics(Queue<byte> packetData)
|
private void HandleLegacyStatistics(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int statCount = dataTypes.ReadNextVarInt(packetData);
|
int statCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
|
|
||||||
|
|
@ -3370,7 +3368,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the Advancements packet (1.12+).
|
/// Handle the Advancements packet (1.12+).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleAdvancements(Queue<byte> packetData)
|
private void HandleAdvancements(PacketReader packetData)
|
||||||
{
|
{
|
||||||
bool reset = dataTypes.ReadNextBool(packetData);
|
bool reset = dataTypes.ReadNextBool(packetData);
|
||||||
|
|
||||||
|
|
@ -3541,14 +3539,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the SelectAdvancementTab packet.
|
/// Handle the SelectAdvancementTab packet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleSelectAdvancementTab(Queue<byte> packetData)
|
private void HandleSelectAdvancementTab(PacketReader packetData)
|
||||||
{
|
{
|
||||||
bool hasTab = dataTypes.ReadNextBool(packetData);
|
bool hasTab = dataTypes.ReadNextBool(packetData);
|
||||||
string? tabId = hasTab ? dataTypes.ReadNextString(packetData) : null;
|
string? tabId = hasTab ? dataTypes.ReadNextString(packetData) : null;
|
||||||
handler.OnSelectAdvancementTab(tabId);
|
handler.OnSelectAdvancementTab(tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleUnlockRecipes(Queue<byte> packetData)
|
private void HandleUnlockRecipes(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int action = dataTypes.ReadNextVarInt(packetData);
|
int action = dataTypes.ReadNextVarInt(packetData);
|
||||||
if (!SkipRecipeBookSettings(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);
|
int entryCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
RecipeBookRecipeEntry[] recipeEntries = new RecipeBookRecipeEntry[entryCount];
|
RecipeBookRecipeEntry[] recipeEntries = new RecipeBookRecipeEntry[entryCount];
|
||||||
|
|
@ -3593,7 +3591,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
handler.OnRecipeBookAdd(recipeEntries, replace);
|
handler.OnRecipeBookAdd(recipeEntries, replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string[] ReadRecipeBookRecipeIds(Queue<byte> packetData)
|
private string[] ReadRecipeBookRecipeIds(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int recipeCount = dataTypes.ReadNextVarInt(packetData);
|
int recipeCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
string[] recipeIds = new string[recipeCount];
|
string[] recipeIds = new string[recipeCount];
|
||||||
|
|
@ -3604,7 +3602,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return recipeIds;
|
return recipeIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string[] ReadRecipeBookDisplayIds(Queue<byte> packetData)
|
private string[] ReadRecipeBookDisplayIds(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int recipeCount = dataTypes.ReadNextVarInt(packetData);
|
int recipeCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
string[] recipeIds = new string[recipeCount];
|
string[] recipeIds = new string[recipeCount];
|
||||||
|
|
@ -3615,7 +3613,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return recipeIds;
|
return recipeIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RecipeBookRecipeEntry ReadRecipeBookDisplayEntry(Queue<byte> packetData)
|
private RecipeBookRecipeEntry ReadRecipeBookDisplayEntry(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int displayId = dataTypes.ReadNextVarInt(packetData);
|
int displayId = dataTypes.ReadNextVarInt(packetData);
|
||||||
string resultLabel = ReadRecipeDisplayResultLabel(packetData);
|
string resultLabel = ReadRecipeDisplayResultLabel(packetData);
|
||||||
|
|
@ -3629,7 +3627,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return new RecipeBookRecipeEntry(commandId, displayText);
|
return new RecipeBookRecipeEntry(commandId, displayText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadRecipeDisplayResultLabel(Queue<byte> packetData)
|
private string ReadRecipeDisplayResultLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int displayType = dataTypes.ReadNextVarInt(packetData);
|
int displayType = dataTypes.ReadNextVarInt(packetData);
|
||||||
return displayType switch
|
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);
|
int ingredientCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
for (int i = 0; i < ingredientCount; i++)
|
for (int i = 0; i < ingredientCount; i++)
|
||||||
|
|
@ -3654,7 +3652,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadShapedRecipeDisplayResultLabel(Queue<byte> packetData)
|
private string ReadShapedRecipeDisplayResultLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
_ = dataTypes.ReadNextVarInt(packetData); // width
|
_ = dataTypes.ReadNextVarInt(packetData); // width
|
||||||
_ = dataTypes.ReadNextVarInt(packetData); // height
|
_ = dataTypes.ReadNextVarInt(packetData); // height
|
||||||
|
|
@ -3667,7 +3665,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadFurnaceRecipeDisplayResultLabel(Queue<byte> packetData)
|
private string ReadFurnaceRecipeDisplayResultLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
_ = ReadSlotDisplayLabel(packetData); // ingredient
|
_ = ReadSlotDisplayLabel(packetData); // ingredient
|
||||||
_ = ReadSlotDisplayLabel(packetData); // fuel
|
_ = ReadSlotDisplayLabel(packetData); // fuel
|
||||||
|
|
@ -3678,7 +3676,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadStonecutterRecipeDisplayResultLabel(Queue<byte> packetData)
|
private string ReadStonecutterRecipeDisplayResultLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
_ = ReadSlotDisplayLabel(packetData); // input
|
_ = ReadSlotDisplayLabel(packetData); // input
|
||||||
string result = ReadSlotDisplayLabel(packetData);
|
string result = ReadSlotDisplayLabel(packetData);
|
||||||
|
|
@ -3686,7 +3684,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadSmithingRecipeDisplayResultLabel(Queue<byte> packetData)
|
private string ReadSmithingRecipeDisplayResultLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
_ = ReadSlotDisplayLabel(packetData); // template
|
_ = ReadSlotDisplayLabel(packetData); // template
|
||||||
_ = ReadSlotDisplayLabel(packetData); // base
|
_ = ReadSlotDisplayLabel(packetData); // base
|
||||||
|
|
@ -3696,7 +3694,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int slotDisplayType = dataTypes.ReadNextVarInt(packetData);
|
int slotDisplayType = dataTypes.ReadNextVarInt(packetData);
|
||||||
|
|
||||||
|
|
@ -3739,7 +3737,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a with_any_potion slot display (26.1+): contains a nested SlotDisplay.
|
/// Reads a with_any_potion slot display (26.1+): contains a nested SlotDisplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ReadWithAnyPotionSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadWithAnyPotionSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
return ReadSlotDisplayLabel(packetData);
|
return ReadSlotDisplayLabel(packetData);
|
||||||
}
|
}
|
||||||
|
|
@ -3747,7 +3745,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads an only_with_component slot display (26.1+): contains a nested SlotDisplay and a DataComponentType VarInt ID.
|
/// Reads an only_with_component slot display (26.1+): contains a nested SlotDisplay and a DataComponentType VarInt ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ReadOnlyWithComponentSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadOnlyWithComponentSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
string sourceLabel = ReadSlotDisplayLabel(packetData);
|
string sourceLabel = ReadSlotDisplayLabel(packetData);
|
||||||
_ = dataTypes.ReadNextVarInt(packetData); // DataComponentType registry id
|
_ = dataTypes.ReadNextVarInt(packetData); // DataComponentType registry id
|
||||||
|
|
@ -3757,14 +3755,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a dyed slot display (26.1+): contains two nested SlotDisplays (dye + target).
|
/// Reads a dyed slot display (26.1+): contains two nested SlotDisplays (dye + target).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ReadDyedSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadDyedSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
_ = ReadSlotDisplayLabel(packetData); // dye
|
_ = ReadSlotDisplayLabel(packetData); // dye
|
||||||
string targetLabel = ReadSlotDisplayLabel(packetData); // target
|
string targetLabel = ReadSlotDisplayLabel(packetData); // target
|
||||||
return targetLabel;
|
return targetLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadSmithingTrimSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadSmithingTrimSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
string baseLabel = ReadSlotDisplayLabel(packetData);
|
string baseLabel = ReadSlotDisplayLabel(packetData);
|
||||||
_ = ReadSlotDisplayLabel(packetData); // material
|
_ = ReadSlotDisplayLabel(packetData); // material
|
||||||
|
|
@ -3772,14 +3770,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return baseLabel;
|
return baseLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadWithRemainderSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadWithRemainderSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
string inputLabel = ReadSlotDisplayLabel(packetData);
|
string inputLabel = ReadSlotDisplayLabel(packetData);
|
||||||
_ = ReadSlotDisplayLabel(packetData); // remainder
|
_ = ReadSlotDisplayLabel(packetData); // remainder
|
||||||
return inputLabel;
|
return inputLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadCompositeSlotDisplayLabel(Queue<byte> packetData)
|
private string ReadCompositeSlotDisplayLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int optionCount = dataTypes.ReadNextVarInt(packetData);
|
int optionCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
string label = "Composite";
|
string label = "Composite";
|
||||||
|
|
@ -3798,12 +3796,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// Read an ItemStackTemplate (26.1+) which encodes fields in a different order
|
/// Read an ItemStackTemplate (26.1+) which encodes fields in a different order
|
||||||
/// than ItemStack: item_id (VarInt), count (VarInt), DataComponentPatch.
|
/// than ItemStack: item_id (VarInt), count (VarInt), DataComponentPatch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ReadItemStackTemplateLabel(Queue<byte> packetData)
|
private string ReadItemStackTemplateLabel(PacketReader packetData)
|
||||||
{
|
{
|
||||||
return dataTypes.ReadNextItemStackTemplate(packetData, itemPalette).GetTypeString();
|
return dataTypes.ReadNextItemStackTemplate(packetData, itemPalette).GetTypeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SkipOptionalCraftingRequirements(Queue<byte> packetData)
|
private void SkipOptionalCraftingRequirements(PacketReader packetData)
|
||||||
{
|
{
|
||||||
if (!dataTypes.ReadNextBool(packetData))
|
if (!dataTypes.ReadNextBool(packetData))
|
||||||
return;
|
return;
|
||||||
|
|
@ -3813,7 +3811,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
SkipItemHolderSet(packetData);
|
SkipItemHolderSet(packetData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SkipItemHolderSet(Queue<byte> packetData)
|
private void SkipItemHolderSet(PacketReader packetData)
|
||||||
{
|
{
|
||||||
int entryCount = dataTypes.ReadNextVarInt(packetData) - 1;
|
int entryCount = dataTypes.ReadNextVarInt(packetData) - 1;
|
||||||
if (entryCount == -1)
|
if (entryCount == -1)
|
||||||
|
|
@ -3826,12 +3824,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
_ = dataTypes.ReadNextVarInt(packetData);
|
_ = 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.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.
|
// MC 1.14+ expands this to 8 booleans by adding blast furnace and smoker states.
|
||||||
int boolCount = protocolVersion >= MC_1_14_Version ? 8 : 4;
|
int boolCount = protocolVersion >= MC_1_14_Version ? 8 : 4;
|
||||||
if (packetData.Count < boolCount)
|
if (packetData.RemainingLength < boolCount)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (int i = 0; i < boolCount; i++)
|
for (int i = 0; i < boolCount; i++)
|
||||||
|
|
@ -3896,9 +3894,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
SendPacket(packetPalette.GetOutgoingIdByType(packet), packetData);
|
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;
|
return;
|
||||||
|
|
||||||
int blockEntityCount = dataTypes.ReadNextVarInt(packetData);
|
int blockEntityCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
|
|
@ -4057,7 +4055,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
// 3. Encryption Request - 9. Login Acknowledged
|
// 3. Encryption Request - 9. Login Acknowledged
|
||||||
while (true)
|
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)
|
switch (packetId)
|
||||||
{
|
{
|
||||||
|
|
@ -4195,7 +4195,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
int loopPrevention = ushort.MaxValue;
|
int loopPrevention = ushort.MaxValue;
|
||||||
while (true)
|
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)
|
if (packetId < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150)
|
||||||
{
|
{
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost,
|
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost,
|
||||||
|
|
@ -4356,11 +4358,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
var statusRequest = DataTypes.GetVarInt(0);
|
var statusRequest = DataTypes.GetVarInt(0);
|
||||||
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
|
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
|
||||||
|
|
||||||
var (statusPacketId, packetData) = socketWrapper.GetNextPacket(-1, dataTypes);
|
IncomingPacket statusPacket = socketWrapper.GetNextPacket(-1, dataTypes);
|
||||||
if (statusPacketId != 0x00)
|
if (statusPacket.PacketId != 0x00)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the Json data
|
// Get the Json data
|
||||||
|
var packetData = statusPacket.CreateReader();
|
||||||
var result = dataTypes.ReadNextString(packetData);
|
var result = dataTypes.ReadNextString(packetData);
|
||||||
|
|
||||||
if (Config.Logging.DebugMessages)
|
if (Config.Logging.DebugMessages)
|
||||||
|
|
@ -4436,9 +4439,10 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload));
|
var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload));
|
||||||
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest));
|
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest));
|
||||||
|
|
||||||
var (pongPacketId, pongPacketData) = socketWrapper.GetNextPacket(-1, dataTypes);
|
IncomingPacket pongPacket = socketWrapper.GetNextPacket(-1, dataTypes);
|
||||||
if (pongPacketId == 0x01)
|
if (pongPacket.PacketId == 0x01)
|
||||||
{
|
{
|
||||||
|
var pongPacketData = pongPacket.CreateReader();
|
||||||
long pongPayload = dataTypes.ReadNextLong(pongPacketData);
|
long pongPayload = dataTypes.ReadNextLong(pongPacketData);
|
||||||
pingMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - pingPayload;
|
pingMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - pingPayload;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MinecraftClient.Protocol.Handlers.Forge;
|
using MinecraftClient.Protocol.Handlers.Forge;
|
||||||
using MinecraftClient.Protocol.Message;
|
using MinecraftClient.Protocol.Message;
|
||||||
|
using MinecraftClient.Protocol.PacketPipeline;
|
||||||
using MinecraftClient.Scripting;
|
using MinecraftClient.Scripting;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
@ -52,7 +53,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
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
|
if (packetID == 0x40) // Disconnect
|
||||||
{
|
{
|
||||||
|
|
@ -93,7 +96,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="packetData">Packet data to read from</param>
|
/// <param name="packetData">Packet data to read from</param>
|
||||||
/// <returns>Length from packet data</returns>
|
/// <returns>Length from packet data</returns>
|
||||||
public int ReadNextVarShort(Queue<byte> packetData)
|
public int ReadNextVarShort(PacketReader packetData)
|
||||||
{
|
{
|
||||||
if (ForgeEnabled())
|
if (ForgeEnabled())
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +117,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="packetData">Plugin message data</param>
|
/// <param name="packetData">Plugin message data</param>
|
||||||
/// <param name="currentDimension">Current world dimension</param>
|
/// <param name="currentDimension">Current world dimension</param>
|
||||||
/// <returns>TRUE if the plugin message was recognized and handled</returns>
|
/// <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)
|
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="packetData">Plugin message data</param>
|
||||||
/// <param name="responseData">Response data to return to server</param>
|
/// <param name="responseData">Response data to return to server</param>
|
||||||
/// <returns>TRUE/FALSE depending on whether the packet was understood or not</returns>
|
/// <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")
|
if (ForgeEnabled() && (forgeInfo!.Version == FMLVersion.FML2 || forgeInfo!.Version == FMLVersion.FML3) && channel == "fml:loginwrapper")
|
||||||
{
|
{
|
||||||
|
|
@ -332,7 +335,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
// FML3 specific,
|
// FML3 specific,
|
||||||
List<string> dataPackRegistries = new();
|
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);
|
int dataPackRegistryCount = dataTypes.ReadNextVarInt(packetData);
|
||||||
for (int i = 0; i < dataPackRegistryCount; i++)
|
for (int i = 0; i < dataPackRegistryCount; i++)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
||||||
//using System.Linq;
|
//using System.Linq;
|
||||||
//using System.Text;
|
//using System.Text;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
|
using MinecraftClient.Protocol.PacketPipeline;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
|
|
@ -23,7 +24,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cache">Cache for reading data</param>
|
/// <param name="cache">Cache for reading data</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
private Chunk? ReadBlockStatesField(Queue<byte> cache)
|
private Chunk? ReadBlockStatesField(PacketReader cache)
|
||||||
{
|
{
|
||||||
// read Block states (Type: Paletted Container)
|
// read Block states (Type: Paletted Container)
|
||||||
byte bitsPerEntry = dataTypes.ReadNextByte(cache);
|
byte bitsPerEntry = dataTypes.ReadNextByte(cache);
|
||||||
|
|
@ -134,7 +135,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache for reading chunk data</param>
|
/// <param name="cache">Cache for reading chunk data</param>
|
||||||
/// <param name="cancellationToken">token to cancel the task</param>
|
/// <param name="cancellationToken">token to cancel the task</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[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();
|
World world = handler.GetWorld();
|
||||||
|
|
||||||
|
|
@ -236,7 +237,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache for reading chunk data</param>
|
/// <param name="cache">Cache for reading chunk data</param>
|
||||||
/// <param name="cancellationToken">token to cancel the task</param>
|
/// <param name="cancellationToken">token to cancel the task</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[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();
|
World world = handler.GetWorld();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,24 +107,24 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return Array.Empty<byte>();
|
return Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Tuple<int, Queue<byte>> GetNextPacket(int compressionThreshold, DataTypes dataTypes)
|
internal IncomingPacket GetNextPacket(int compressionThreshold, DataTypes dataTypes)
|
||||||
{
|
{
|
||||||
int packetLength = ReadNextVarIntRaw();
|
int packetLength = ReadNextVarIntRaw();
|
||||||
using PacketReadStream packetStream = new(readStream, packetLength);
|
using PacketReadStream packetStream = new(readStream, packetLength);
|
||||||
byte[] payload = ReadPacketPayload(packetStream, compressionThreshold);
|
byte[] payload = ReadPacketPayload(packetStream, compressionThreshold);
|
||||||
Queue<byte> packetData = new(payload);
|
var packetData = new PacketReader(payload);
|
||||||
int packetId = dataTypes.ReadNextVarInt(packetData);
|
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);
|
int packetLength = await ReadNextVarIntRawAsync(cancellationToken);
|
||||||
await using PacketReadStream packetStream = new(readStream, packetLength);
|
await using PacketReadStream packetStream = new(readStream, packetLength);
|
||||||
byte[] payload = await ReadPacketPayloadAsync(packetStream, compressionThreshold, cancellationToken);
|
byte[] payload = await ReadPacketPayloadAsync(packetStream, compressionThreshold, cancellationToken);
|
||||||
Queue<byte> packetData = new(payload);
|
var packetData = new PacketReader(payload);
|
||||||
int packetId = dataTypes.ReadNextVarInt(packetData);
|
int packetId = dataTypes.ReadNextVarInt(packetData);
|
||||||
return new(packetId, packetData);
|
return new(packetId, packetData.CopyRemaining());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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