mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
More parts switched to async
This commit is contained in:
parent
297d7dbc30
commit
4d800a31fc
6 changed files with 295 additions and 73 deletions
|
|
@ -409,6 +409,9 @@ internal sealed class DeterministicCapabilities : IMccMcpCapabilities
|
|||
playerLocation = new { x = C(0.5), y = C(80.0), z = C(0.5) }
|
||||
});
|
||||
|
||||
public Task<MccMcpResult> DigBlockAsync(double x, double y, double z, double durationSeconds) =>
|
||||
Task.FromResult(DigBlock(x, y, z, durationSeconds));
|
||||
|
||||
public MccMcpResult PlaceBlock(int x, int y, int z, string face, string hand, bool lookAtBlock) =>
|
||||
MccMcpResult.Ok(new { success = true, x, y, z, face, hand, lookAtBlock, action = "place_block" });
|
||||
|
||||
|
|
@ -797,6 +800,9 @@ internal sealed class DeterministicCapabilities : IMccMcpCapabilities
|
|||
preferStack
|
||||
});
|
||||
|
||||
public Task<MccMcpResult> DropInventoryItemAsync(string itemType, int count, int inventoryId, bool preferStack) =>
|
||||
Task.FromResult(DropInventoryItem(itemType, count, inventoryId, preferStack));
|
||||
|
||||
public MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) =>
|
||||
MccMcpResult.Ok(new
|
||||
{
|
||||
|
|
@ -815,6 +821,9 @@ internal sealed class DeterministicCapabilities : IMccMcpCapabilities
|
|||
touchedTargetSlots = new[] { 0 }
|
||||
});
|
||||
|
||||
public Task<MccMcpResult> DepositContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack) =>
|
||||
Task.FromResult(DepositContainerItem(itemType, count, inventoryId, preferLargestStack));
|
||||
|
||||
public MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack) =>
|
||||
MccMcpResult.Ok(new
|
||||
{
|
||||
|
|
@ -833,6 +842,9 @@ internal sealed class DeterministicCapabilities : IMccMcpCapabilities
|
|||
touchedTargetSlots = new[] { 36 }
|
||||
});
|
||||
|
||||
public Task<MccMcpResult> WithdrawContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack) =>
|
||||
Task.FromResult(WithdrawContainerItem(itemType, count, inventoryId, preferLargestStack));
|
||||
|
||||
public MccMcpResult QueryEntities(int maxCount) =>
|
||||
MccMcpResult.Ok(new
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public interface IMccMcpCapabilities
|
|||
MccMcpResult SelectHotbarItem(string itemType, bool preferLowestSlot);
|
||||
MccMcpResult UseItemOnBlock(double x, double y, double z);
|
||||
MccMcpResult DigBlock(double x, double y, double z, double durationSeconds);
|
||||
Task<MccMcpResult> DigBlockAsync(double x, double y, double z, double durationSeconds);
|
||||
MccMcpResult PlaceBlock(int x, int y, int z, string face, string hand, bool lookAtBlock);
|
||||
MccMcpResult InteractEntity(int entityId, string interaction, string hand);
|
||||
MccMcpResult AttackEntity(int entityId);
|
||||
|
|
@ -60,8 +61,11 @@ public interface IMccMcpCapabilities
|
|||
Task<MccMcpResult> CloseContainerAsync(int inventoryId, int timeoutMs);
|
||||
MccMcpResult InventoryWindowAction(int inventoryId, int slotId, string actionType);
|
||||
MccMcpResult DropInventoryItem(string itemType, int count, int inventoryId, bool preferStack);
|
||||
Task<MccMcpResult> DropInventoryItemAsync(string itemType, int count, int inventoryId, bool preferStack);
|
||||
MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack);
|
||||
Task<MccMcpResult> DepositContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack);
|
||||
MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack);
|
||||
Task<MccMcpResult> WithdrawContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack);
|
||||
MccMcpResult QueryEntities(int maxCount);
|
||||
MccMcpResult ListEntities(int maxCount, string? typeFilter, double radius);
|
||||
MccMcpResult GetEntityInfo(int entityId, bool includeMetadata, bool includeEquipment, bool includeEffects);
|
||||
|
|
|
|||
|
|
@ -861,6 +861,11 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
|
||||
public MccMcpResult DigBlock(double x, double y, double z, double durationSeconds)
|
||||
{
|
||||
return DigBlockAsync(x, y, z, durationSeconds).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<MccMcpResult> DigBlockAsync(double x, double y, double z, double durationSeconds)
|
||||
{
|
||||
if (!IsCategoryEnabled(t => t.Movement))
|
||||
return MccMcpResult.Fail("capability_disabled");
|
||||
|
|
@ -923,7 +928,10 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
if (!accepted)
|
||||
continue;
|
||||
|
||||
if (WaitForBlockChange(client, target, beforeBlock, GetDigVerifyWaitMs(attemptDuration), out afterBlock))
|
||||
(bool blockChanged, Block updatedBlock) =
|
||||
await WaitForBlockChangeAsync(client, target, beforeBlock, GetDigVerifyWaitMs(attemptDuration));
|
||||
afterBlock = updatedBlock;
|
||||
if (blockChanged)
|
||||
{
|
||||
changed = true;
|
||||
break;
|
||||
|
|
@ -1590,6 +1598,11 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
|
||||
public MccMcpResult DropInventoryItem(string itemType, int count, int inventoryId, bool preferStack)
|
||||
{
|
||||
return DropInventoryItemAsync(itemType, count, inventoryId, preferStack).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<MccMcpResult> DropInventoryItemAsync(string itemType, int count, int inventoryId, bool preferStack)
|
||||
{
|
||||
if (!IsCategoryEnabled(t => t.Inventory))
|
||||
return MccMcpResult.Fail("capability_disabled");
|
||||
|
|
@ -1612,10 +1625,10 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
});
|
||||
}
|
||||
|
||||
return client.InvokeOnMainThread(() =>
|
||||
return await Task.Run(async () =>
|
||||
{
|
||||
Dictionary<int, Container> inventories = client.GetInventories();
|
||||
if (!inventories.TryGetValue(inventoryId, out Container? inventory))
|
||||
Container? inventory = client.GetInventory(inventoryId);
|
||||
if (inventory is null)
|
||||
return MccMcpResult.Fail("invalid_state");
|
||||
|
||||
int cursorCount = GetCursorItemCount(inventory, parsedItemType);
|
||||
|
|
@ -1659,7 +1672,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
|
||||
int dropFromSlot = Math.Min(remaining, currentItem.Count);
|
||||
touchedSlots.Add(entry.slot);
|
||||
bool ok = TryDropInventorySlotItems(client, inventoryId, inventory, entry.slot, parsedItemType, dropFromSlot, out int droppedFromSlot);
|
||||
(bool ok, int droppedFromSlot) = await TryDropInventorySlotItemsAsync(client, inventoryId, inventory, entry.slot, parsedItemType, dropFromSlot);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
|
|
@ -1725,12 +1738,22 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
|
||||
public MccMcpResult DepositContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack)
|
||||
{
|
||||
return TransferContainerItem(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Deposit);
|
||||
return DepositContainerItemAsync(itemType, count, inventoryId, preferLargestStack).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<MccMcpResult> DepositContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack)
|
||||
{
|
||||
return TransferContainerItemAsync(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Deposit);
|
||||
}
|
||||
|
||||
public MccMcpResult WithdrawContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack)
|
||||
{
|
||||
return TransferContainerItem(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Withdraw);
|
||||
return WithdrawContainerItemAsync(itemType, count, inventoryId, preferLargestStack).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<MccMcpResult> WithdrawContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack)
|
||||
{
|
||||
return TransferContainerItemAsync(itemType, count, inventoryId, preferLargestStack, InventoryTransferDirection.Withdraw);
|
||||
}
|
||||
|
||||
public MccMcpResult QueryEntities(int maxCount)
|
||||
|
|
@ -1957,6 +1980,11 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
|
||||
private MccMcpResult TransferContainerItem(string itemType, int count, int inventoryId, bool preferLargestStack, InventoryTransferDirection direction)
|
||||
{
|
||||
return TransferContainerItemAsync(itemType, count, inventoryId, preferLargestStack, direction).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private async Task<MccMcpResult> TransferContainerItemAsync(string itemType, int count, int inventoryId, bool preferLargestStack, InventoryTransferDirection direction)
|
||||
{
|
||||
if (!IsCategoryEnabled(t => t.Inventory))
|
||||
return MccMcpResult.Fail("capability_disabled");
|
||||
|
|
@ -2078,7 +2106,18 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
|
||||
if (direction == InventoryTransferDirection.Withdraw)
|
||||
{
|
||||
if (!WaitForRangeCount(client, resolvedInventoryId, parsedItemType, sourceStart, sourceEnd, countAfterShift => countAfterShift < beforeSourceCount, DefaultInventoryActionWaitMs, out Container? afterShift, out int afterSourceCount))
|
||||
Container? afterShift = null;
|
||||
int afterSourceCount = beforeSourceCount;
|
||||
if (!await WaitForRangeCountAsync(client, resolvedInventoryId, parsedItemType, sourceStart, sourceEnd, countAfterShift => countAfterShift < beforeSourceCount, DefaultInventoryActionWaitMs, result =>
|
||||
{
|
||||
afterShift = result.Inventory;
|
||||
afterSourceCount = result.ItemCount;
|
||||
},
|
||||
onInitialize: () =>
|
||||
{
|
||||
afterShift = null;
|
||||
afterSourceCount = beforeSourceCount;
|
||||
}))
|
||||
{
|
||||
afterShift = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId));
|
||||
afterSourceCount = afterShift is null ? beforeSourceCount : CountItemInRange(afterShift, parsedItemType, sourceStart, sourceEnd);
|
||||
|
|
@ -2088,7 +2127,18 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!WaitForRangeCount(client, resolvedInventoryId, parsedItemType, targetStart, targetEnd, countAfterShift => countAfterShift > beforeTargetCount, DefaultInventoryActionWaitMs, out Container? afterShift, out int afterTargetCount))
|
||||
Container? afterShift = null;
|
||||
int afterTargetCount = beforeTargetCount;
|
||||
if (!await WaitForRangeCountAsync(client, resolvedInventoryId, parsedItemType, targetStart, targetEnd, countAfterShift => countAfterShift > beforeTargetCount, DefaultInventoryActionWaitMs, result =>
|
||||
{
|
||||
afterShift = result.Inventory;
|
||||
afterTargetCount = result.ItemCount;
|
||||
},
|
||||
onInitialize: () =>
|
||||
{
|
||||
afterShift = null;
|
||||
afterTargetCount = beforeTargetCount;
|
||||
}))
|
||||
{
|
||||
afterShift = client.InvokeOnMainThread(() => client.GetInventory(resolvedInventoryId));
|
||||
afterTargetCount = afterShift is null ? beforeTargetCount : CountItemInRange(afterShift, parsedItemType, targetStart, targetEnd);
|
||||
|
|
@ -2100,7 +2150,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
if (direction == InventoryTransferDirection.Withdraw && movedCount > remaining)
|
||||
{
|
||||
int excessCount = movedCount - remaining;
|
||||
MccMcpResult returnExcess = TransferContainerItem(parsedItemType.ToString(), excessCount, resolvedInventoryId, preferLargestStack, InventoryTransferDirection.Deposit);
|
||||
MccMcpResult returnExcess = await TransferContainerItemAsync(parsedItemType.ToString(), excessCount, resolvedInventoryId, preferLargestStack, InventoryTransferDirection.Deposit);
|
||||
if (!returnExcess.Success)
|
||||
{
|
||||
return MccMcpResult.Fail("action_incomplete", data: new
|
||||
|
|
@ -2121,7 +2171,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
else
|
||||
{
|
||||
movedCount = TransferPartialFromSlot(
|
||||
movedCount = await TransferPartialFromSlotAsync(
|
||||
client,
|
||||
resolvedInventoryId,
|
||||
slot,
|
||||
|
|
@ -2405,16 +2455,10 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
|
||||
private static bool TryDropInventorySlotItems(McClient client, int inventoryId, Container inventory, int slotId, ItemType itemType, int dropCount, out int droppedCount)
|
||||
{
|
||||
droppedCount = 0;
|
||||
if (!inventory.Items.TryGetValue(slotId, out Item? currentItem) || currentItem.Type != itemType || currentItem.Count <= 0)
|
||||
return false;
|
||||
|
||||
if (inventoryId == 0 && inventory.IsHotbar(slotId, out int hotbarSlot))
|
||||
{
|
||||
return TryDropHotbarSlotItems(client, slotId, hotbarSlot, itemType, dropCount, currentItem.Count, out droppedCount);
|
||||
}
|
||||
|
||||
return TryDropWindowSlotItems(client, inventoryId, slotId, itemType, dropCount, currentItem.Count, out 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)
|
||||
|
|
@ -2438,7 +2482,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
.ToArray();
|
||||
}
|
||||
|
||||
private static int TransferPartialFromSlot(McClient client, int inventoryId, int sourceSlot, ItemType itemType, int requestedCount, int sourceStart, int sourceEnd, int targetStart, int targetEnd, List<int> touchedTargetSlots)
|
||||
private static async Task<int> TransferPartialFromSlotAsync(McClient client, int inventoryId, int sourceSlot, ItemType itemType, int requestedCount, int sourceStart, int sourceEnd, int targetStart, int targetEnd, List<int> touchedTargetSlots)
|
||||
{
|
||||
Container? inventory = client.InvokeOnMainThread(() => client.GetInventory(inventoryId));
|
||||
if (inventory is null || !inventory.Items.TryGetValue(sourceSlot, out Item? sourceItem) || sourceItem.Count <= 0)
|
||||
|
|
@ -2448,7 +2492,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
if (!client.DoWindowAction(inventoryId, sourceSlot, WindowActionType.LeftClick))
|
||||
return 0;
|
||||
|
||||
if (!WaitForCursorItem(client, itemType, DefaultInventoryActionWaitMs, out _))
|
||||
if (!await WaitForCursorItemAsync(client, itemType, DefaultInventoryActionWaitMs))
|
||||
return 0;
|
||||
|
||||
int moved = 0;
|
||||
|
|
@ -2467,7 +2511,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
if (step <= 0 || !PlaceItemsFromCursor(client, inventoryId, targetSlot, step))
|
||||
break;
|
||||
|
||||
if (!WaitForPlacement(client, inventoryId, targetSlot, itemType, beforeTargetCount, beforeCursorCount, step))
|
||||
if (!await WaitForPlacementAsync(client, inventoryId, targetSlot, itemType, beforeTargetCount, beforeCursorCount, step))
|
||||
break;
|
||||
|
||||
touchedTargetSlots.Add(targetSlot);
|
||||
|
|
@ -2484,7 +2528,7 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
if (!client.DoWindowAction(inventoryId, returnSlot, WindowActionType.LeftClick))
|
||||
return 0;
|
||||
|
||||
if (!WaitForCursorClear(client, DefaultInventoryActionWaitMs))
|
||||
if (!await WaitForCursorClearAsync(client, DefaultInventoryActionWaitMs))
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2570,27 +2614,27 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
return inventory.Items.TryGetValue(slot, out Item? item) && item.Type == itemType ? item.Count : 0;
|
||||
}
|
||||
|
||||
private static bool TryDropHotbarSlotItems(McClient client, int slotId, int hotbarSlot, ItemType itemType, int dropCount, int availableInSlot, out int droppedCount)
|
||||
private static async Task<(bool Success, int DroppedCount)> TryDropHotbarSlotItemsAsync(McClient client, int slotId, int hotbarSlot, ItemType itemType, int dropCount, int availableInSlot)
|
||||
{
|
||||
droppedCount = 0;
|
||||
byte previousSlot = client.GetCurrentSlot();
|
||||
int droppedCount = 0;
|
||||
byte previousSlot = client.InvokeOnMainThread(client.GetCurrentSlot);
|
||||
bool restoreSlot = previousSlot != hotbarSlot;
|
||||
|
||||
if (restoreSlot && !client.ChangeSlot((short)hotbarSlot))
|
||||
return false;
|
||||
return (false, 0);
|
||||
|
||||
try
|
||||
{
|
||||
if (dropCount >= availableInSlot)
|
||||
{
|
||||
if (!client.DropSelectedItem(dropEntireStack: true))
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
if (!WaitForSlotItemCount(client, 0, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs, out _, out _))
|
||||
return false;
|
||||
if (!await WaitForSlotItemCountAsync(client, 0, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs))
|
||||
return (false, droppedCount);
|
||||
|
||||
droppedCount = availableInSlot;
|
||||
return true;
|
||||
return (true, droppedCount);
|
||||
}
|
||||
|
||||
int remaining = dropCount;
|
||||
|
|
@ -2598,23 +2642,23 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
{
|
||||
Container? currentInventory = client.GetInventory(0);
|
||||
if (currentInventory is null)
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
int beforeSlotCount = GetSlotItemCount(currentInventory, slotId, itemType);
|
||||
if (beforeSlotCount <= 0)
|
||||
break;
|
||||
|
||||
if (!client.DropSelectedItem(dropEntireStack: false))
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
if (!WaitForSlotItemCount(client, 0, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs, out _, out _))
|
||||
return false;
|
||||
if (!await WaitForSlotItemCountAsync(client, 0, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs))
|
||||
return (false, droppedCount);
|
||||
|
||||
remaining--;
|
||||
droppedCount++;
|
||||
}
|
||||
|
||||
return remaining == 0;
|
||||
return (remaining == 0, droppedCount);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -2623,20 +2667,20 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
}
|
||||
|
||||
private static bool TryDropWindowSlotItems(McClient client, int inventoryId, int slotId, ItemType itemType, int dropCount, int availableInSlot, out int droppedCount)
|
||||
private static async Task<(bool Success, int DroppedCount)> TryDropWindowSlotItemsAsync(McClient client, int inventoryId, int slotId, ItemType itemType, int dropCount, int availableInSlot)
|
||||
{
|
||||
droppedCount = 0;
|
||||
int droppedCount = 0;
|
||||
|
||||
if (dropCount >= availableInSlot)
|
||||
{
|
||||
if (!client.DoWindowAction(inventoryId, slotId, WindowActionType.DropItemStack))
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
if (!WaitForSlotItemCount(client, inventoryId, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs, out _, out _))
|
||||
return false;
|
||||
if (!await WaitForSlotItemCountAsync(client, inventoryId, slotId, itemType, count => count == 0, DefaultInventoryActionWaitMs))
|
||||
return (false, droppedCount);
|
||||
|
||||
droppedCount = availableInSlot;
|
||||
return true;
|
||||
return (true, droppedCount);
|
||||
}
|
||||
|
||||
int remaining = dropCount;
|
||||
|
|
@ -2644,23 +2688,143 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
{
|
||||
Container? currentInventory = client.GetInventory(inventoryId);
|
||||
if (currentInventory is null)
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
int beforeSlotCount = GetSlotItemCount(currentInventory, slotId, itemType);
|
||||
if (beforeSlotCount <= 0)
|
||||
break;
|
||||
|
||||
if (!client.DoWindowAction(inventoryId, slotId, WindowActionType.DropItem))
|
||||
return false;
|
||||
return (false, droppedCount);
|
||||
|
||||
if (!WaitForSlotItemCount(client, inventoryId, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs, out _, out _))
|
||||
return false;
|
||||
if (!await WaitForSlotItemCountAsync(client, inventoryId, slotId, itemType, count => count <= beforeSlotCount - 1, DefaultInventoryActionWaitMs))
|
||||
return (false, droppedCount);
|
||||
|
||||
remaining--;
|
||||
droppedCount++;
|
||||
}
|
||||
|
||||
return remaining == 0;
|
||||
return (remaining == 0, droppedCount);
|
||||
}
|
||||
|
||||
private static Task<(bool Success, int DroppedCount)> TryDropInventorySlotItemsAsync(McClient client, int inventoryId, Container inventory, int slotId, ItemType itemType, int dropCount)
|
||||
{
|
||||
if (!inventory.Items.TryGetValue(slotId, out Item? currentItem) || currentItem.Type != itemType || currentItem.Count <= 0)
|
||||
return Task.FromResult((false, 0));
|
||||
|
||||
if (inventoryId == 0 && inventory.IsHotbar(slotId, out int hotbarSlot))
|
||||
return TryDropHotbarSlotItemsAsync(client, slotId, hotbarSlot, itemType, dropCount, currentItem.Count);
|
||||
|
||||
return TryDropWindowSlotItemsAsync(client, inventoryId, slotId, itemType, dropCount, currentItem.Count);
|
||||
}
|
||||
|
||||
private readonly record struct InventoryCountState(Container? Inventory, int ItemCount);
|
||||
|
||||
private static async Task<bool> WaitForCursorItemAsync(McClient client, ItemType itemType, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
if (TryGetCursorItem(client, out Item? cursorItem) && cursorItem is not null && cursorItem.Type == itemType)
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForCursorClearAsync(McClient client, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
if (!TryGetCursorItem(client, out _))
|
||||
return true;
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForPlacementAsync(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.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;
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForSlotItemCountAsync(McClient client, int inventoryId, int slotId, ItemType itemType, Func<int, bool> predicate, int waitMs)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
Container? inventory = client.GetInventory(inventoryId);
|
||||
if (inventory is not null)
|
||||
{
|
||||
int itemCount = GetSlotItemCount(inventory, slotId, itemType);
|
||||
if (predicate(itemCount))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForRangeCountAsync(McClient client, int inventoryId, ItemType itemType, int startSlot, int endSlot, Func<int, bool> predicate, int waitMs, Action<InventoryCountState> onObserved, Action onInitialize)
|
||||
{
|
||||
onInitialize();
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(waitMs);
|
||||
while (true)
|
||||
{
|
||||
Container? inventory = client.GetInventory(inventoryId);
|
||||
int itemCount = 0;
|
||||
if (inventory is not null)
|
||||
{
|
||||
itemCount = CountItemInRange(inventory, itemType, startSlot, endSlot);
|
||||
onObserved(new InventoryCountState(inventory, itemCount));
|
||||
if (predicate(itemCount))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return false;
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitForCursorItem(McClient client, ItemType itemType, int waitMs, out Item? cursorItem)
|
||||
|
|
@ -3040,6 +3204,24 @@ public sealed class MccMcpCapabilities : IMccMcpCapabilities
|
|||
}
|
||||
}
|
||||
|
||||
private static async Task<(bool Changed, Block AfterBlock)> WaitForBlockChangeAsync(McClient client, Location target, Block beforeBlock, int waitMs)
|
||||
{
|
||||
Block 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, afterBlock);
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
return (false, afterBlock);
|
||||
|
||||
await Task.Delay(ArrivalPollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreEquivalentBlocks(Block left, Block right)
|
||||
{
|
||||
return left.BlockId == right.BlockId
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ public sealed class MccMcpToolSet
|
|||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_dig_block"), Description("Dig a block at target location.")]
|
||||
public object DigBlock(double x, double y, double z, double durationSeconds = 0)
|
||||
public async Task<object> DigBlock(double x, double y, double z, double durationSeconds = 0)
|
||||
{
|
||||
return capabilities.DigBlock(x, y, z, durationSeconds);
|
||||
return await capabilities.DigBlockAsync(x, y, z, durationSeconds);
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_place_block"), Description("Place the currently held block/item at a target block location.")]
|
||||
|
|
@ -329,33 +329,33 @@ public sealed class MccMcpToolSet
|
|||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_inventory_drop_item"), Description("Drop an exact item count from an inventory by item type.")]
|
||||
public object InventoryDropItem(
|
||||
public async Task<object> InventoryDropItem(
|
||||
[Description("Item type enum name (e.g. Diamond).")] string itemType,
|
||||
[Description("Exact number of items to drop.")] int count,
|
||||
[Description("Inventory ID. 0 is the player inventory.")] int inventoryId = 0,
|
||||
[Description("Prefer dropping from larger stacks first when true.")] bool preferStack = false)
|
||||
{
|
||||
return capabilities.DropInventoryItem(itemType, count, inventoryId, preferStack);
|
||||
return await capabilities.DropInventoryItemAsync(itemType, count, inventoryId, preferStack);
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_container_deposit_item"), Description("Move an exact item count from the player inventory into an open container and verify the transfer.")]
|
||||
public object ContainerDepositItem(
|
||||
public async Task<object> ContainerDepositItem(
|
||||
[Description("Item type enum name (e.g. Diamond).")] string itemType,
|
||||
[Description("Exact number of items to move into the container.")] int count,
|
||||
[Description("Container inventory ID, or -1 for the active non-player container.")] int inventoryId = -1,
|
||||
[Description("Prefer larger source stacks first when true.")] bool preferLargestStack = true)
|
||||
{
|
||||
return capabilities.DepositContainerItem(itemType, count, inventoryId, preferLargestStack);
|
||||
return await capabilities.DepositContainerItemAsync(itemType, count, inventoryId, preferLargestStack);
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_container_withdraw_item"), Description("Move an exact item count from an open container into the player inventory and verify the transfer.")]
|
||||
public object ContainerWithdrawItem(
|
||||
public async Task<object> ContainerWithdrawItem(
|
||||
[Description("Item type enum name (e.g. Diamond).")] string itemType,
|
||||
[Description("Exact number of items to move into the player inventory.")] int count,
|
||||
[Description("Container inventory ID, or -1 for the active non-player container.")] int inventoryId = -1,
|
||||
[Description("Prefer larger source stacks first when true.")] bool preferLargestStack = true)
|
||||
{
|
||||
return capabilities.WithdrawContainerItem(itemType, count, inventoryId, preferLargestStack);
|
||||
return await capabilities.WithdrawContainerItemAsync(itemType, count, inventoryId, preferLargestStack);
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_entities_query"), Description("Query tracked entities.")]
|
||||
|
|
|
|||
|
|
@ -3974,6 +3974,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <returns>True if login successful</returns>
|
||||
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
|
||||
{
|
||||
return LoginAsync(playerKeyPair, session, isTransfer).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private async Task<bool> LoginAsync(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
|
||||
{
|
||||
int nextState = isTransfer && protocolVersion >= MC_1_20_6_Version ? 3 : 2;
|
||||
|
||||
|
|
@ -4052,7 +4057,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// 3. Encryption Request - 9. Login Acknowledged
|
||||
while (true)
|
||||
{
|
||||
var (packetId, packetData) = ReadNextPacket();
|
||||
var (packetId, packetData) = await ReadNextPacketAsync(CancellationToken.None);
|
||||
|
||||
switch (packetId)
|
||||
{
|
||||
|
|
@ -4075,7 +4080,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (protocolVersion >= MC_1_20_6_Version)
|
||||
shouldAuthetnicate = dataTypes.ReadNextBool(packetData);
|
||||
|
||||
return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(),
|
||||
return await StartEncryptionAsync(handler.GetUserUuidStr(), handler.GetSessionID(),
|
||||
Config.Main.General.AccountType, token, serverId,
|
||||
serverPublicKey, playerKeyPair, session, shouldAuthetnicate);
|
||||
}
|
||||
|
|
@ -4091,7 +4096,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (protocolVersion >= MC_1_20_2_Version)
|
||||
SendPacket(0x03, new List<byte>());
|
||||
|
||||
if (!pForge.CompleteForgeHandshake())
|
||||
if (!await pForge.CompleteForgeHandshakeAsync())
|
||||
{
|
||||
log.Error($"§8{Translations.error_forge}");
|
||||
return false;
|
||||
|
|
@ -4112,7 +4117,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// Start network encryption. Automatically called by Login() if the server requests encryption.
|
||||
/// </summary>
|
||||
/// <returns>True if encryption was successful</returns>
|
||||
private bool StartEncryption(string uuid, string sessionID, LoginType type, byte[] token, string serverIDhash,
|
||||
private async Task<bool> StartEncryptionAsync(string uuid, string sessionID, LoginType type, byte[] token, string serverIDhash,
|
||||
byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session, bool shouldAuthetnicate)
|
||||
{
|
||||
var RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!;
|
||||
|
|
@ -4140,7 +4145,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (needCheckSession)
|
||||
{
|
||||
var serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey);
|
||||
if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash, type))
|
||||
if (await ProtocolHandler.SessionCheckAsync(uuid, sessionID, serverHash, type))
|
||||
{
|
||||
session.ServerIDhash = serverIDhash;
|
||||
session.ServerPublicKey = serverPublicKey;
|
||||
|
|
@ -4190,7 +4195,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
int loopPrevention = ushort.MaxValue;
|
||||
while (true)
|
||||
{
|
||||
var (packetId, packetData) = ReadNextPacket();
|
||||
var (packetId, packetData) = await ReadNextPacketAsync(CancellationToken.None);
|
||||
if (packetId < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150)
|
||||
{
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost,
|
||||
|
|
@ -4241,7 +4246,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
handler.OnLoginSuccess(uuidReceived, userName, playerProperty);
|
||||
|
||||
if (!pForge.CompleteForgeHandshake())
|
||||
if (!await pForge.CompleteForgeHandshakeAsync())
|
||||
{
|
||||
log.Error($"§8{Translations.error_forge_encrypt}");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
using MinecraftClient.Scripting;
|
||||
|
|
@ -21,6 +22,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
private readonly ForgeInfo? forgeInfo = forgeInfo;
|
||||
private FMLHandshakeClientState fmlHandshakeState = FMLHandshakeClientState.START;
|
||||
private DateTime? pendingServerDataAckAt;
|
||||
private bool ForgeEnabled() { return forgeInfo is not null; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -40,12 +42,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <returns>Whether the handshake was successful.</returns>
|
||||
public bool CompleteForgeHandshake()
|
||||
{
|
||||
return CompleteForgeHandshakeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<bool> CompleteForgeHandshakeAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML)
|
||||
{
|
||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
(int packetID, Queue<byte> packetData) = protocol18.ReadNextPacket();
|
||||
(int packetID, Queue<byte> packetData) = await protocol18.ReadNextPacketAsync(cancellationToken);
|
||||
|
||||
if (packetID == 0x40) // Disconnect
|
||||
{
|
||||
|
|
@ -56,12 +63,31 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
// Send back regular packet to the vanilla protocol handler
|
||||
protocol18.HandlePacket(packetID, packetData);
|
||||
await FlushPendingHandshakeActionsAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task FlushPendingHandshakeActionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!pendingServerDataAckAt.HasValue)
|
||||
return;
|
||||
|
||||
TimeSpan delay = pendingServerDataAckAt.Value - DateTime.UtcNow;
|
||||
if (delay > TimeSpan.Zero)
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_accept, acceptnewlines: true);
|
||||
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA });
|
||||
|
||||
pendingServerDataAckAt = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read Forge VarShort field
|
||||
/// </summary>
|
||||
|
|
@ -145,16 +171,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (discriminator != FMLHandshakeDiscriminator.ModList)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_accept, acceptnewlines: true);
|
||||
// Tell the server that yes, we are OK with the mods it has
|
||||
// even though we don't actually care what mods it has.
|
||||
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA });
|
||||
|
||||
pendingServerDataAckAt = DateTime.UtcNow.AddSeconds(2);
|
||||
fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE;
|
||||
return false;
|
||||
case FMLHandshakeClientState.WAITINGSERVERCOMPLETE:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue