Merge branch 'stable' into next-minor

This commit is contained in:
Dylan K. Taylor 2022-03-23 13:39:41 +00:00
commit a9f2766a8b
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
6 changed files with 102 additions and 57 deletions

View File

@ -55,6 +55,25 @@ abstract class BaseInventory implements Inventory{
return $this->maxStackSize;
}
public function setMaxStackSize(int $size) : void{
$this->maxStackSize = $size;
}
abstract protected function internalSetItem(int $index, Item $item) : void;
public function setItem(int $index, Item $item) : void{
if($item->isNull()){
$item = VanillaItems::AIR();
}else{
$item = clone $item;
}
$oldItem = $this->getItem($index);
$this->internalSetItem($index, $item);
$this->onSlotChange($index, $oldItem);
}
/**
* @param Item[] $items
*/
@ -85,21 +104,6 @@ abstract class BaseInventory implements Inventory{
$this->onContentChange($oldContents);
}
abstract protected function internalSetItem(int $index, Item $item) : void;
public function setItem(int $index, Item $item) : void{
if($item->isNull()){
$item = VanillaItems::AIR();
}else{
$item = clone $item;
}
$oldItem = $this->getItem($index);
$this->internalSetItem($index, $item);
$this->onSlotChange($index, $oldItem);
}
public function contains(Item $item) : bool{
$count = max(1, $item->getCount());
$checkDamage = !$item->hasAnyDamageValue();
@ -128,18 +132,6 @@ abstract class BaseInventory implements Inventory{
return $slots;
}
public function remove(Item $item) : void{
$checkDamage = !$item->hasAnyDamageValue();
$checkTags = $item->hasNamedTag();
foreach($this->getContents() as $index => $i){
if($item->equals($i, $checkDamage, $checkTags)){
$this->clear($index);
}
}
}
public function first(Item $item, bool $exact = false) : int{
$count = $exact ? $item->getCount() : max(1, $item->getCount());
$checkDamage = $exact || !$item->hasAnyDamageValue();
@ -253,6 +245,17 @@ abstract class BaseInventory implements Inventory{
return $slot;
}
public function remove(Item $item) : void{
$checkDamage = !$item->hasAnyDamageValue();
$checkTags = $item->hasNamedTag();
foreach($this->getContents() as $index => $i){
if($item->equals($i, $checkDamage, $checkTags)){
$this->clear($index);
}
}
}
public function removeItem(Item ...$slots) : array{
/** @var Item[] $itemSlots */
/** @var Item[] $slots */
@ -323,10 +326,6 @@ abstract class BaseInventory implements Inventory{
}
}
public function setMaxStackSize(int $size) : void{
$this->maxStackSize = $size;
}
public function onOpen(Player $who) : void{
$this->viewers[spl_object_id($who)] = $who;
}

View File

@ -46,6 +46,16 @@ interface Inventory{
*/
public function setItem(int $index, Item $item) : void;
/**
* @return Item[]
*/
public function getContents(bool $includeEmpty = false) : array;
/**
* @param Item[] $items
*/
public function setContents(array $items) : void;
/**
* Stores the given Items in the inventory. This will try to fill
* existing stacks and empty slots as well as it can.
@ -66,24 +76,6 @@ interface Inventory{
*/
public function getAddableItemQuantity(Item $item) : int;
/**
* Removes the given Item from the inventory.
* It will return the Items that couldn't be removed.
*
* @return Item[]
*/
public function removeItem(Item ...$slots) : array;
/**
* @return Item[]
*/
public function getContents(bool $includeEmpty = false) : array;
/**
* @param Item[] $items
*/
public function setContents(array $items) : void;
/**
* Checks if the inventory contains any Item with the same material data.
* It will check id, amount, and metadata (if not null)
@ -121,6 +113,14 @@ interface Inventory{
*/
public function remove(Item $item) : void;
/**
* Removes the given Item from the inventory.
* It will return the Items that couldn't be removed.
*
* @return Item[]
*/
public function removeItem(Item ...$slots) : array;
/**
* Will clear a specific slot
*/

View File

@ -52,6 +52,10 @@ class SimpleInventory extends BaseInventory{
return $this->slots[$index] !== null ? clone $this->slots[$index] : VanillaItems::AIR();
}
protected function internalSetItem(int $index, Item $item) : void{
$this->slots[$index] = $item->isNull() ? null : $item;
}
/**
* @return Item[]
*/
@ -78,8 +82,4 @@ class SimpleInventory extends BaseInventory{
}
}
}
protected function internalSetItem(int $index, Item $item) : void{
$this->slots[$index] = $item->isNull() ? null : $item;
}
}

View File

@ -59,7 +59,9 @@ use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\ObjectSet;
use function array_map;
use function array_search;
use function get_class;
use function max;
use function spl_object_id;
/**
* @phpstan-type ContainerOpenClosure \Closure(int $id, Inventory $inventory) : (list<ClientboundPacket>|null)
@ -304,6 +306,28 @@ class InventoryManager{
}
}
public function syncMismatchedPredictedSlotChanges() : void{
foreach($this->initiatedSlotChanges as $windowId => $slots){
if(!isset($this->windowMap[$windowId])){
continue;
}
$inventory = $this->windowMap[$windowId];
foreach($slots as $slot => $expectedItem){
if(!$inventory->slotExists($slot)){
continue; //TODO: size desync ???
}
$actualItem = $inventory->getItem($slot);
if(!$actualItem->equalsExact($expectedItem)){
$this->session->getLogger()->debug("Detected prediction mismatch in inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " slot $slot");
$this->syncSlot($inventory, $slot);
}
}
}
$this->initiatedSlotChanges = [];
}
public function syncData(Inventory $inventory, int $propertyId, int $value) : void{
$windowId = $this->getWindowId($inventory);
if($windowId !== null){

View File

@ -234,9 +234,13 @@ class InGamePacketHandler extends PacketHandler{
$packetHandled = true;
$useItemTransaction = $packet->getItemInteractionData();
if($useItemTransaction !== null && !$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
$packetHandled = false;
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
if($useItemTransaction !== null){
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
$packetHandled = false;
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
}else{
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
}
}
$blockActions = $packet->getBlockActions();
@ -304,6 +308,8 @@ class InGamePacketHandler extends PacketHandler{
if(!$result){
$this->inventoryManager->syncAll();
}else{
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
}
return $result;
}

View File

@ -313,6 +313,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->setNameTag($this->username);
}
private function callDummyItemHeldEvent() : void{
$slot = $this->inventory->getHeldItemIndex();
$event = new PlayerItemHeldEvent($this, $this->inventory->getItem($slot), $slot);
$event->call();
//TODO: this event is actually cancellable, but cancelling it here has no meaningful result, so we
//just ignore it. We fire this only because the content of the held slot changed, not because the
//held slot index changed. We can't prevent that from here, and nor would it be sensible to.
}
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);
$this->addDefaultWindows();
@ -321,10 +331,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
function(Inventory $unused, int $slot) : void{
if($slot === $this->inventory->getHeldItemIndex()){
$this->setUsingItem(false);
$this->callDummyItemHeldEvent();
}
},
function() : void{
function(Inventory $unused, array $changedSlots) : void{
$this->setUsingItem(false);
$heldSlot = $this->inventory->getHeldItemIndex();
if(isset($changedSlots[$heldSlot])){
$this->callDummyItemHeldEvent();
}
}
));