Cleaned up implementations for EnderChestInventory/DoubleChestInventory

previously, these were forced to extend BaseInventory because of the amount of crap in Inventory's interface.
This meant that these inventories had their own slots storage, which would be _mostly_ unused because these inventories aren't real inventories, but rather just delegates.
This lead to a variety of bugs in the past, such as certain API methods on BaseInventory not working correctly for DoubleChestInventory in particular.

Now, BaseInventory just implements the functional part of the inventory implementation, leaving the storage system up to the implementation.
A SimpleInventory class is provided with a simple SplFixedArray storage backing, which is used by most inventories.
EnderChestInventory and DoubleChestInventory now extend BaseInventory directly, and implement custom methods for dealing with their delegates.
This commit is contained in:
Dylan K. Taylor
2021-05-09 20:50:40 +01:00
parent 1533789f35
commit f909557529
22 changed files with 237 additions and 99 deletions

View File

@ -27,7 +27,14 @@ use pocketmine\player\Player;
use pocketmine\world\sound\Sound;
use function count;
abstract class AnimatedBlockInventory extends BlockInventory{
trait AnimatedBlockInventoryTrait{
use BlockInventoryTrait;
/**
* @return Player[]
* @phpstan-return array<int, Player>
*/
abstract public function getViewers() : array;
abstract protected function getOpenSound() : Sound;

View File

@ -23,13 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\world\Position;
class AnvilInventory extends BlockInventory{
class AnvilInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder){
parent::__construct($holder, 2);
$this->holder = $holder;
parent::__construct(2);
}
public function onClose(Player $who) : void{

View File

@ -24,15 +24,18 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\block\Barrel;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
use pocketmine\world\sound\BarrelCloseSound;
use pocketmine\world\sound\BarrelOpenSound;
use pocketmine\world\sound\Sound;
class BarrelInventory extends AnimatedBlockInventory{
class BarrelInventory extends SimpleInventory implements BlockInventory{
use AnimatedBlockInventoryTrait;
public function __construct(Position $holder){
parent::__construct($holder, 27);
$this->holder = $holder;
parent::__construct(27);
}
protected function getOpenSound() : Sound{

View File

@ -23,22 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\BaseInventory;
use pocketmine\world\Position;
class BlockInventory extends BaseInventory{
/** @var Position */
protected $holder;
public function __construct(Position $holder, int $size){
$this->holder = $holder->asPosition();
parent::__construct($size);
}
/**
* @return Position
*/
public function getHolder(){
return $this->holder;
}
interface BlockInventory{
public function getHolder() : Position;
}

View File

@ -0,0 +1,34 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\world\Position;
trait BlockInventoryTrait{
protected Position $holder;
public function getHolder() : Position{
return $this->holder;
}
}

View File

@ -23,11 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class BrewingStandInventory extends BlockInventory{
class BrewingStandInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder, int $size = 5){
parent::__construct($holder, $size);
$this->holder = $holder;
parent::__construct($size);
}
}

View File

@ -23,16 +23,19 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\world\Position;
use pocketmine\world\sound\ChestCloseSound;
use pocketmine\world\sound\ChestOpenSound;
use pocketmine\world\sound\Sound;
class ChestInventory extends AnimatedBlockInventory{
class ChestInventory extends SimpleInventory implements BlockInventory{
use AnimatedBlockInventoryTrait;
public function __construct(Position $holder){
parent::__construct($holder, 27);
$this->holder = $holder;
parent::__construct(27);
}
protected function getOpenSound() : Sound{
@ -43,7 +46,7 @@ class ChestInventory extends AnimatedBlockInventory{
return new ChestCloseSound();
}
protected function animateBlock(bool $isOpen) : void{
public function animateBlock(bool $isOpen) : void{
$holder = $this->getHolder();
//event ID is always 1 for a chest

View File

@ -23,11 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\BaseInventory;
use pocketmine\inventory\InventoryHolder;
use pocketmine\item\Item;
use pocketmine\world\sound\ChestCloseSound;
use pocketmine\world\sound\ChestOpenSound;
use pocketmine\world\sound\Sound;
class DoubleChestInventory extends AnimatedBlockInventory implements InventoryHolder{
class DoubleChestInventory extends BaseInventory implements BlockInventory, InventoryHolder{
use AnimatedBlockInventoryTrait;
/** @var ChestInventory */
private $left;
/** @var ChestInventory */
@ -36,21 +41,24 @@ class DoubleChestInventory extends AnimatedBlockInventory implements InventoryHo
public function __construct(ChestInventory $left, ChestInventory $right){
$this->left = $left;
$this->right = $right;
parent::__construct($this->left->getHolder(), $this->left->getSize() + $this->right->getSize());
$this->holder = $this->left->getHolder();
parent::__construct();
}
public function getInventory(){
return $this;
}
public function getSize() : int{
return $this->left->getSize() + $this->right->getSize();
}
public function getItem(int $index) : Item{
return $index < $this->left->getSize() ? $this->left->getItem($index) : $this->right->getItem($index - $this->left->getSize());
}
public function setItem(int $index, Item $item) : void{
$old = $this->getItem($index);
protected function internalSetItem(int $index, Item $item) : void{
$index < $this->left->getSize() ? $this->left->setItem($index, $item) : $this->right->setItem($index - $this->left->getSize(), $item);
$this->onSlotChange($index, $old);
}
public function getContents(bool $includeEmpty = false) : array{
@ -64,9 +72,26 @@ class DoubleChestInventory extends AnimatedBlockInventory implements InventoryHo
return $result;
}
protected function getOpenSound() : Sound{ return $this->left->getOpenSound(); }
protected function internalSetContents(array $items) : void{
$leftSize = $this->left->getSize();
protected function getCloseSound() : Sound{ return $this->left->getCloseSound(); }
$leftContents = [];
$rightContents = [];
foreach($items as $i => $item){
if($i < $this->left->getSize()){
$leftContents[$i] = $item;
}else{
$rightContents[$i - $leftSize] = $item;
}
}
$this->left->setContents($leftContents);
$this->right->setContents($rightContents);
}
protected function getOpenSound() : Sound{ return new ChestOpenSound(); }
protected function getCloseSound() : Sound{ return new ChestCloseSound(); }
protected function animateBlock(bool $isOpen) : void{
$this->left->animateBlock($isOpen);

View File

@ -23,13 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\world\Position;
class EnchantInventory extends BlockInventory{
class EnchantInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder){
parent::__construct($holder, 2);
$this->holder = $holder;
parent::__construct(2);
}
public function onClose(Player $who) : void{

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\BaseInventory;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\InventoryListener;
@ -38,13 +39,17 @@ use pocketmine\world\sound\Sound;
/**
* EnderChestInventory is not a real inventory; it's just a gateway to the player's ender inventory.
*/
class EnderChestInventory extends AnimatedBlockInventory{
class EnderChestInventory extends BaseInventory implements BlockInventory{
use AnimatedBlockInventoryTrait {
onClose as animatedBlockInventoryTrait_onClose;
}
private PlayerEnderInventory $inventory;
private InventoryListener $inventoryListener;
public function __construct(Position $holder, PlayerEnderInventory $inventory){
parent::__construct($holder, $inventory->getSize());
parent::__construct();
$this->holder = $holder;
$this->inventory = $inventory;
$this->inventory->getListeners()->add($this->inventoryListener = new CallbackInventoryListener(
function(Inventory $unused, int $slot, Item $oldItem) : void{
@ -60,11 +65,15 @@ class EnderChestInventory extends AnimatedBlockInventory{
return $this->inventory;
}
public function getSize() : int{
return $this->inventory->getSize();
}
public function getItem(int $index) : Item{
return $this->inventory->getItem($index);
}
public function setItem(int $index, Item $item) : void{
protected function internalSetItem(int $index, Item $item) : void{
$this->inventory->setItem($index, $item);
}
@ -72,7 +81,7 @@ class EnderChestInventory extends AnimatedBlockInventory{
return $this->inventory->getContents($includeEmpty);
}
public function setContents(array $items) : void{
protected function internalSetContents(array $items) : void{
$this->inventory->setContents($items);
}
@ -92,7 +101,7 @@ class EnderChestInventory extends AnimatedBlockInventory{
}
public function onClose(Player $who) : void{
parent::onClose($who);
$this->animatedBlockInventoryTrait_onClose($who);
if($who === $this->inventory->getHolder()){
$this->inventory->getListeners()->remove($this->inventoryListener);
$this->inventoryListener = CallbackInventoryListener::onAnyChange(static function() : void{}); //break cyclic reference

View File

@ -23,13 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\item\Item;
use pocketmine\world\Position;
class FurnaceInventory extends BlockInventory{
class FurnaceInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder){
parent::__construct($holder, 3);
$this->holder = $holder;
parent::__construct(3);
}
public function getResult() : Item{

View File

@ -23,11 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class HopperInventory extends BlockInventory{
class HopperInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder, int $size = 5){
parent::__construct($holder, $size);
$this->holder = $holder;
parent::__construct($size);
}
}

View File

@ -23,16 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\player\Player;
use pocketmine\world\Position;
final class LoomInventory extends BlockInventory{
final class LoomInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public const SLOT_BANNER = 0;
public const SLOT_DYE = 1;
public const SLOT_PATTERN = 2;
public function __construct(Position $holder, int $size = 3){
parent::__construct($holder, $size);
$this->holder = $holder;
parent::__construct($size);
}
public function onClose(Player $who) : void{