InGamePacketHandler: fixed dropping items from unselected hotbar slots

This commit is contained in:
Dylan K. Taylor 2023-03-21 14:45:18 +00:00
parent 043e81e737
commit ea386c42d3
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -386,40 +386,52 @@ class InGamePacketHandler extends PacketHandler{
throw new PacketHandlingException("Expected exactly 2 actions for dropping an item"); throw new PacketHandlingException("Expected exactly 2 actions for dropping an item");
} }
$sourceSlot = null;
$clientItemStack = null;
$droppedCount = null;
foreach($data->getActions() as $networkInventoryAction){ foreach($data->getActions() as $networkInventoryAction){
if($networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_WORLD && $networkInventoryAction->inventorySlot == NetworkInventoryAction::ACTION_MAGIC_SLOT_DROP_ITEM){ if($networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_WORLD && $networkInventoryAction->inventorySlot == NetworkInventoryAction::ACTION_MAGIC_SLOT_DROP_ITEM){
//drop item - we don't need to validate this, we only care about the count $droppedCount = $networkInventoryAction->newItem->getItemStack()->getCount();
//if the resulting actions don't match the client for some reason, it will trigger an automatic if($droppedCount <= 0){
//prediction rollback anyway.
//it's technically possible to see this more than once, but a normal client should never do that.
$droppedItemStack = $networkInventoryAction->newItem->getItemStack();
if($droppedItemStack->getCount() <= 0){
throw new PacketHandlingException("Expected positive count for dropped item"); throw new PacketHandlingException("Expected positive count for dropped item");
} }
}elseif($networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && $networkInventoryAction->windowId === ContainerIds::INVENTORY){
//mobile players can drop an item from a non-selected hotbar slot
$sourceSlot = $networkInventoryAction->inventorySlot;
$clientItemStack = $networkInventoryAction->oldItem->getItemStack();
}else{
throw new PacketHandlingException("Unexpected action type in drop item transaction");
}
}
if($sourceSlot === null || $clientItemStack === null || $droppedCount === null){
throw new PacketHandlingException("Missing information in drop item transaction, need source slot, client item stack and dropped count");
}
$inventory = $this->player->getInventory(); $inventory = $this->player->getInventory();
$heldItem = $inventory->getItemInHand(); if(!$inventory->slotExists($sourceSlot)){
$heldItemStack = TypeConverter::getInstance()->coreItemStackToNet($heldItem); return false; //TODO: size desync??
}
$sourceSlotItem = $inventory->getItem($sourceSlot);
$serverItemStack = TypeConverter::getInstance()->coreItemStackToNet($sourceSlotItem);
//because the client doesn't tell us the expected itemstack ID, we have to deep-compare our known //because the client doesn't tell us the expected itemstack ID, we have to deep-compare our known
//itemstack info with the one the client sent. This is costly, but we don't have any other option :( //itemstack info with the one the client sent. This is costly, but we don't have any other option :(
if($heldItemStack->getCount() < $droppedItemStack->getCount() || !$heldItemStack->equalsWithoutCount($droppedItemStack)){ if(!$serverItemStack->equals($clientItemStack)){
return false; return false;
} }
//this modifies $heldItem //this modifies $sourceSlotItem
$droppedItem = $heldItem->pop($droppedItemStack->getCount()); $droppedItem = $sourceSlotItem->pop($droppedCount);
$builder = new TransactionBuilder(); $builder = new TransactionBuilder();
$builder->getInventory($inventory)->setItem($inventory->getHeldItemIndex(), $heldItem); $builder->getInventory($inventory)->setItem($sourceSlot, $sourceSlotItem);
$builder->addAction(new DropItemAction($droppedItem)); $builder->addAction(new DropItemAction($droppedItem));
$transaction = new InventoryTransaction($this->player, $builder->generateActions()); $transaction = new InventoryTransaction($this->player, $builder->generateActions());
return $this->executeInventoryTransaction($transaction, $itemStackRequestId); return $this->executeInventoryTransaction($transaction, $itemStackRequestId);
} }
}
throw new PacketHandlingException("Legacy 'normal' transactions should only be used for dropping items");
}
private function handleUseItemTransaction(UseItemTransactionData $data) : bool{ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{
$this->player->selectHotbarSlot($data->getHotbarSlot()); $this->player->selectHotbarSlot($data->getHotbarSlot());