InventoryManager: disentangle slot tracking from slot syncing

This commit is contained in:
Dylan K. Taylor 2023-01-06 20:26:19 +00:00
parent d3cea2ca7c
commit 8633804f15
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 62 additions and 57 deletions

View File

@ -348,7 +348,7 @@ abstract class BaseInventory implements Inventory{
if($invManager === null){ if($invManager === null){
continue; continue;
} }
$invManager->syncSlot($this, $index); $invManager->onSlotChange($this, $index);
} }
} }

View File

@ -382,21 +382,36 @@ class InventoryManager{
} }
} }
public function syncSlot(Inventory $inventory, int $slot) : void{ public function onSlotChange(Inventory $inventory, int $slot) : void{
$slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null;
if($slotMap !== null){
$windowId = ContainerIds::UI;
$netSlot = $slotMap->mapCoreToNet($slot) ?? null;
}else{
$windowId = $this->getWindowId($inventory);
$netSlot = $slot;
}
if($windowId !== null && $netSlot !== null){
$currentItem = $inventory->getItem($slot); $currentItem = $inventory->getItem($slot);
$predictions = $this->initiatedSlotChanges[spl_object_id($inventory)] ?? null; $predictions = $this->initiatedSlotChanges[spl_object_id($inventory)] ?? null;
$clientSideItem = $predictions?->getSlot($slot); $clientSideItem = $predictions?->getSlot($slot);
if($clientSideItem === null || !$clientSideItem->equalsExact($currentItem)){ if($clientSideItem === null || !$clientSideItem->equalsExact($currentItem)){
$itemStackWrapper = $this->wrapItemStack($inventory, $slot, $currentItem); //no prediction or incorrect - do not associate this with the currently active itemstack request
$this->trackItemStack($inventory, $slot, $currentItem, null);
$this->syncSlot($inventory, $slot);
}else{
//correctly predicted - associate the change with the currently active itemstack request
$this->trackItemStack($inventory, $slot, $currentItem, $this->currentItemStackRequestId);
}
$predictions?->remove($slot);
}
public function syncSlot(Inventory $inventory, int $slot) : void{
$itemStackInfo = $this->getItemStackInfo($inventory, $slot);
if($itemStackInfo === null){
throw new \LogicException("Cannot sync an untracked inventory slot");
}
$slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null;
if($slotMap !== null){
$windowId = ContainerIds::UI;
$netSlot = $slotMap->mapCoreToNet($slot) ?? throw new AssumptionFailedError("We already have an ItemStackInfo, so this should not be null");
}else{
$windowId = $this->getWindowId($inventory) ?? throw new AssumptionFailedError("We already have an ItemStackInfo, so this should not be null");
$netSlot = $slot;
}
$itemStackWrapper = new ItemStackWrapper($itemStackInfo->getStackId(), $itemStackInfo->getItemStack());
if($windowId === ContainerIds::OFFHAND){ if($windowId === ContainerIds::OFFHAND){
//TODO: HACK! //TODO: HACK!
//The client may sometimes ignore the InventorySlotPacket for the offhand slot. //The client may sometimes ignore the InventorySlotPacket for the offhand slot.
@ -422,12 +437,9 @@ class InventoryManager{
} }
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper)); $this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper));
} }
}elseif($this->currentItemStackRequestId !== null){ $predictions = $this->initiatedSlotChanges[spl_object_id($inventory)] ?? null;
$this->trackItemStack($inventory, $slot, $currentItem, $this->currentItemStackRequestId);
}
$predictions?->remove($slot); $predictions?->remove($slot);
} }
}
public function syncContents(Inventory $inventory) : void{ public function syncContents(Inventory $inventory) : void{
$slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null; $slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null;
@ -437,26 +449,25 @@ class InventoryManager{
$windowId = $this->getWindowId($inventory); $windowId = $this->getWindowId($inventory);
} }
if($windowId !== null){ if($windowId !== null){
unset($this->initiatedSlotChanges[spl_object_id($inventory)]);
$contents = [];
foreach($inventory->getContents(true) as $slot => $item){
$info = $this->trackItemStack($inventory, $slot, $item, null);
$contents[] = new ItemStackWrapper($info->getStackId(), $info->getItemStack());
}
if($slotMap !== null){ if($slotMap !== null){
$predictions = $this->initiatedSlotChanges[spl_object_id($inventory)] ?? null; foreach($contents as $slotId => $info){
foreach($inventory->getContents(true) as $slotId => $item){
$packetSlot = $slotMap->mapCoreToNet($slotId) ?? null; $packetSlot = $slotMap->mapCoreToNet($slotId) ?? null;
if($packetSlot === null){ if($packetSlot === null){
continue; continue;
} }
$predictions?->remove($slotId);
$this->session->sendDataPacket(InventorySlotPacket::create( $this->session->sendDataPacket(InventorySlotPacket::create(
$windowId, $windowId,
$packetSlot, $packetSlot,
$this->wrapItemStack($inventory, $slotId, $inventory->getItem($slotId)) $info
)); ));
} }
}else{ }else{
unset($this->initiatedSlotChanges[spl_object_id($inventory)]);
$contents = [];
foreach($inventory->getContents(true) as $slotId => $item){
$contents[] = $this->wrapItemStack($inventory, $slotId, $item);
}
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, $contents)); $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $contents));
} }
} }
@ -475,16 +486,15 @@ class InventoryManager{
foreach($this->initiatedSlotChanges as $predictions){ foreach($this->initiatedSlotChanges as $predictions){
$inventory = $predictions->getInventory(); $inventory = $predictions->getInventory();
foreach($predictions->getSlots() as $slot => $expectedItem){ foreach($predictions->getSlots() as $slot => $expectedItem){
if(!$inventory->slotExists($slot)){ if(!$inventory->slotExists($slot) || $this->getItemStackInfo($inventory, $slot) === null){
continue; //TODO: size desync ??? continue; //TODO: size desync ???
} }
$actualItem = $inventory->getItem($slot);
if(!$actualItem->equalsExact($expectedItem)){ //any prediction that still exists at this point is a slot that was predicted to change but didn't
$this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot"); $this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot");
$this->syncSlot($inventory, $slot); $this->syncSlot($inventory, $slot);
} }
} }
}
$this->initiatedSlotChanges = []; $this->initiatedSlotChanges = [];
} }
@ -550,11 +560,6 @@ class InventoryManager{
return $this->itemStackInfos[spl_object_id($inventory)][$slotId] = $info; return $this->itemStackInfos[spl_object_id($inventory)][$slotId] = $info;
} }
private function wrapItemStack(Inventory $inventory, int $slotId, Item $item) : ItemStackWrapper{
$info = $this->trackItemStack($inventory, $slotId, $item, null);
return new ItemStackWrapper($info->getStackId(), $info->getItemStack());
}
public function matchItemStack(Inventory $inventory, int $slotId, int $clientItemStackId) : bool{ public function matchItemStack(Inventory $inventory, int $slotId, int $clientItemStackId) : bool{
$inventoryObjectId = spl_object_id($inventory); $inventoryObjectId = spl_object_id($inventory);
if(!isset($this->itemStackInfos[$inventoryObjectId])){ if(!isset($this->itemStackInfos[$inventoryObjectId])){