This commit is contained in:
Dylan K. Taylor
2025-08-31 01:29:09 +01:00
parent d7f86d3172
commit 088bfed820
3 changed files with 23 additions and 31 deletions

View File

@ -157,6 +157,10 @@ class InventoryManager implements InventoryListener{
return $this->entries[spl_object_id($inventory)] ?? null;
}
private function getEntryByWindow(InventoryWindow $window) : ?InventoryManagerEntry{
return $this->getEntry($window->getInventory());
}
public function getInventoryWindow(Inventory $inventory) : ?InventoryWindow{
return $this->getEntry($inventory)?->window;
}
@ -253,7 +257,7 @@ class InventoryManager implements InventoryListener{
private function addPredictedSlotChangeInternal(InventoryWindow $window, int $slot, ItemStack $item) : void{
//TODO: does this need a null check?
$entry = $this->getEntry($window->getInventory()) ?? throw new AssumptionFailedError("Assume this should never be null");
$entry = $this->getEntryByWindow($window) ?? throw new AssumptionFailedError("Assume this should never be null");
$entry->predictions[$slot] = $item;
}
@ -513,15 +517,14 @@ class InventoryManager implements InventoryListener{
}
public function requestSyncSlot(InventoryWindow $window, int $slot) : void{
$inventory = $window->getInventory();
$inventoryEntry = $this->getEntry($inventory);
$inventoryEntry = $this->getEntryByWindow($window);
if($inventoryEntry === null){
//this can happen when an inventory changed during InventoryCloseEvent, or when a temporary inventory
//is cleared before removal.
return;
}
$currentItem = $this->session->getTypeConverter()->coreItemStackToNet($inventory->getItem($slot));
$currentItem = $this->session->getTypeConverter()->coreItemStackToNet($window->getInventory()->getItem($slot));
$clientSideItem = $inventoryEntry->predictions[$slot] ?? null;
if($clientSideItem === null || !$this->itemStacksEqual($currentItem, $clientSideItem)){
//no prediction or incorrect - do not associate this with the currently active itemstack request
@ -586,7 +589,7 @@ class InventoryManager implements InventoryListener{
}
private function syncSlot(InventoryWindow $window, int $slot, ItemStack $itemStack) : void{
$entry = $this->getEntry($window->getInventory()) ?? throw new \LogicException("Cannot sync an untracked inventory");
$entry = $this->getEntryByWindow($window) ?? throw new \LogicException("Cannot sync an untracked inventory");
$itemStackInfo = $entry->itemStackInfos[$slot];
if($itemStackInfo === null){
throw new \LogicException("Cannot sync an untracked inventory slot");
@ -623,8 +626,7 @@ class InventoryManager implements InventoryListener{
}
private function syncContents(InventoryWindow $window) : void{
$inventory = $window->getInventory();
$entry = $this->getEntry($inventory);
$entry = $this->getEntryByWindow($window);
if($entry === null){
//this can happen when an inventory changed during InventoryCloseEvent, or when a temporary inventory
//is cleared before removal.
@ -640,7 +642,7 @@ class InventoryManager implements InventoryListener{
$entry->pendingSyncs = [];
$contents = [];
$typeConverter = $this->session->getTypeConverter();
foreach($inventory->getContents(true) as $slot => $item){
foreach($window->getInventory()->getContents(true) as $slot => $item){
$itemStack = $typeConverter->coreItemStackToNet($item);
$info = $this->trackItemStack($entry, $slot, $itemStack, null);
$contents[] = new ItemStackWrapper($info->getStackId(), $itemStack);
@ -728,7 +730,7 @@ class InventoryManager implements InventoryListener{
$playerInventory = $this->player->getInventory();
$selected = $this->player->getHotbar()->getSelectedIndex();
if($selected !== $this->clientSelectedHotbarSlot){
$inventoryEntry = $this->getEntry($playerInventory) ?? throw new AssumptionFailedError("Player inventory should always be tracked");
$inventoryEntry = $this->getEntryByWindow($this->getInventoryWindow($playerInventory)) ?? throw new AssumptionFailedError("Player inventory should always be tracked");
$itemStackInfo = $inventoryEntry->itemStackInfos[$selected] ?? null;
if($itemStackInfo === null){
throw new AssumptionFailedError("Untracked player inventory slot $selected");
@ -787,8 +789,8 @@ class InventoryManager implements InventoryListener{
return $this->nextItemStackId++;
}
public function getItemStackInfo(Inventory $inventory, int $slot) : ?ItemStackInfo{
return $this->getEntry($inventory)?->itemStackInfos[$slot] ?? null;
public function getItemStackInfo(InventoryWindow $window, int $slot) : ?ItemStackInfo{
return $this->getEntryByWindow($window)?->itemStackInfos[$slot] ?? null;
}
private function trackItemStack(InventoryManagerEntry $entry, int $slotId, ItemStack $itemStack, ?int $itemStackRequestId) : ItemStackInfo{

View File

@ -94,7 +94,7 @@ class ItemStackRequestExecutor{
* @throws ItemStackRequestProcessException
*/
private function matchItemStack(InventoryWindow $window, int $slotId, int $clientItemStackId) : void{
$info = $this->inventoryManager->getItemStackInfo($window->getInventory(), $slotId);
$info = $this->inventoryManager->getItemStackInfo($window, $slotId);
if($info === null){
throw new AssumptionFailedError("The inventory is tracked and the slot is valid, so this should not be null");
}
@ -373,7 +373,6 @@ class ItemStackRequestExecutor{
$predictedDamage = $action->getPredictedDurability();
if($usedItem instanceof Durable && $predictedDamage >= 0 && $predictedDamage <= $usedItem->getMaxDurability()){
$usedItem->setDamage($predictedDamage);
//TODO: this is a bit yucky - maybe we don't need the window to add a predicted slot change?
$inventoryWindow = $this->inventoryManager->getInventoryWindow($inventory) ?? throw new AssumptionFailedError("The player's inventory should always have an inventory window");
$this->inventoryManager->addPredictedSlotChange($inventoryWindow, $slot, $usedItem);
}

View File

@ -31,6 +31,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponse;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseContainerInfo;
use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseSlotInfo;
use pocketmine\player\InventoryWindow;
use pocketmine\utils\AssumptionFailedError;
final class ItemStackResponseBuilder{
@ -51,21 +52,11 @@ final class ItemStackResponseBuilder{
}
/**
* @phpstan-return array{Inventory, int}
* @phpstan-return array{InventoryWindow, int}
*/
private function getInventoryAndSlot(int $containerInterfaceId, int $slotId) : ?array{
private function locateWindowAndSlot(int $containerInterfaceId, int $slotId) : ?array{
[$windowId, $slotId] = ItemStackContainerIdTranslator::translate($containerInterfaceId, $this->inventoryManager->getCurrentWindowId(), $slotId);
$windowAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slotId);
if($windowAndSlot === null){
return null;
}
[$window, $slot] = $windowAndSlot;
$inventory = $window->getInventory();
if(!$inventory->slotExists($slot)){
return null;
}
return [$inventory, $slot];
return $this->inventoryManager->locateWindowAndSlot($windowId, $slotId);
}
public function build() : ItemStackResponse{
@ -75,18 +66,18 @@ final class ItemStackResponseBuilder{
continue;
}
foreach($slotIds as $slotId){
$inventoryAndSlot = $this->getInventoryAndSlot($containerInterfaceId, $slotId);
if($inventoryAndSlot === null){
$windowAndSlot = $this->locateWindowAndSlot($containerInterfaceId, $slotId);
if($windowAndSlot === null){
//a plugin may have closed the inventory during an event, or the slot may have been invalid
continue;
}
[$inventory, $slot] = $inventoryAndSlot;
[$window, $slot] = $windowAndSlot;
$itemStackInfo = $this->inventoryManager->getItemStackInfo($inventory, $slot);
$itemStackInfo = $this->inventoryManager->getItemStackInfo($window, $slot);
if($itemStackInfo === null){
throw new AssumptionFailedError("ItemStackInfo should never be null for an open inventory");
}
$item = $inventory->getItem($slot);
$item = $window->getInventory()->getItem($slot);
$responseInfosByContainer[$containerInterfaceId][] = new ItemStackResponseSlotInfo(
$slotId,