More parts switched to async

This commit is contained in:
Anon 2026-04-04 21:31:02 +02:00
parent 297d7dbc30
commit 4d800a31fc
6 changed files with 295 additions and 73 deletions

View file

@ -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
{

View file

@ -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);

View file

@ -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

View file

@ -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.")]

View file

@ -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;

View file

@ -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: