mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 09:56:06 +00:00
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:
@ -26,7 +26,7 @@ namespace pocketmine\inventory;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class ArmorInventory extends BaseInventory{
|
||||
class ArmorInventory extends SimpleInventory{
|
||||
public const SLOT_HEAD = 0;
|
||||
public const SLOT_CHEST = 1;
|
||||
public const SLOT_LEGS = 2;
|
||||
|
@ -27,21 +27,18 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use function array_map;
|
||||
use function array_slice;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* This class provides everything needed to implement an inventory, minus the underlying storage system.
|
||||
*/
|
||||
abstract class BaseInventory implements Inventory{
|
||||
use InventoryHelpersTrait;
|
||||
|
||||
/** @var int */
|
||||
protected $maxStackSize = Inventory::MAX_STACK;
|
||||
/**
|
||||
* @var \SplFixedArray|(Item|null)[]
|
||||
* @phpstan-var \SplFixedArray<Item|null>
|
||||
*/
|
||||
protected $slots;
|
||||
/** @var Player[] */
|
||||
protected $viewers = [];
|
||||
/**
|
||||
@ -50,42 +47,18 @@ abstract class BaseInventory implements Inventory{
|
||||
*/
|
||||
protected $listeners;
|
||||
|
||||
public function __construct(int $size){
|
||||
$this->slots = new \SplFixedArray($size);
|
||||
public function __construct(){
|
||||
$this->listeners = new ObjectSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the inventory.
|
||||
*/
|
||||
public function getSize() : int{
|
||||
return $this->slots->getSize();
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return $this->maxStackSize;
|
||||
}
|
||||
|
||||
public function getItem(int $index) : Item{
|
||||
return $this->slots[$index] !== null ? clone $this->slots[$index] : ItemFactory::air();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @param Item[] $items
|
||||
*/
|
||||
public function getContents(bool $includeEmpty = false) : array{
|
||||
$contents = [];
|
||||
|
||||
foreach($this->slots as $i => $slot){
|
||||
if($slot !== null){
|
||||
$contents[$i] = clone $slot;
|
||||
}elseif($includeEmpty){
|
||||
$contents[$i] = ItemFactory::air();
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
abstract protected function internalSetContents(array $items) : void;
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
@ -95,22 +68,14 @@ abstract class BaseInventory implements Inventory{
|
||||
$items = array_slice($items, 0, $this->getSize(), true);
|
||||
}
|
||||
|
||||
$oldContents = array_map(function(?Item $item) : Item{
|
||||
return $item ?? ItemFactory::air();
|
||||
}, $this->slots->toArray());
|
||||
$oldContents = $this->getContents(true);
|
||||
|
||||
$listeners = $this->listeners->toArray();
|
||||
$this->listeners->clear();
|
||||
$viewers = $this->viewers;
|
||||
$this->viewers = [];
|
||||
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
if(!isset($items[$i])){
|
||||
$this->clear($i);
|
||||
}else{
|
||||
$this->setItem($i, $items[$i]);
|
||||
}
|
||||
}
|
||||
$this->internalSetContents($items);
|
||||
|
||||
$this->listeners->add(...$listeners); //don't directly write, in case listeners were added while operation was in progress
|
||||
foreach($viewers as $id => $viewer){
|
||||
@ -120,6 +85,8 @@ 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 = ItemFactory::air();
|
||||
@ -129,7 +96,7 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
$oldItem = $this->getItem($index);
|
||||
|
||||
$this->slots[$index] = $item->isNull() ? null : $item;
|
||||
$this->internalSetItem($index, $item);
|
||||
$this->onSlotChange($index, $oldItem);
|
||||
}
|
||||
|
||||
@ -188,7 +155,7 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function slotExists(int $slot) : bool{
|
||||
return $slot >= 0 and $slot < $this->slots->getSize();
|
||||
return $slot >= 0 and $slot < $this->getSize();
|
||||
}
|
||||
|
||||
public function getListeners() : ObjectSet{
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class PlayerCursorInventory extends BaseInventory{
|
||||
class PlayerCursorInventory extends SimpleInventory{
|
||||
/** @var Player */
|
||||
protected $holder;
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\entity\Human;
|
||||
|
||||
final class PlayerEnderInventory extends BaseInventory{
|
||||
final class PlayerEnderInventory extends SimpleInventory{
|
||||
|
||||
private Human $holder;
|
||||
|
||||
|
@ -28,7 +28,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
|
||||
class PlayerInventory extends BaseInventory{
|
||||
class PlayerInventory extends SimpleInventory{
|
||||
|
||||
/** @var Human */
|
||||
protected $holder;
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\entity\Human;
|
||||
|
||||
final class PlayerOffHandInventory extends BaseInventory{
|
||||
final class PlayerOffHandInventory extends SimpleInventory{
|
||||
/** @var Human */
|
||||
private $holder;
|
||||
|
||||
|
85
src/inventory/SimpleInventory.php
Normal file
85
src/inventory/SimpleInventory.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
|
||||
/**
|
||||
* This class provides a complete implementation of a regular inventory.
|
||||
*/
|
||||
class SimpleInventory extends BaseInventory{
|
||||
/**
|
||||
* @var \SplFixedArray|(Item|null)[]
|
||||
* @phpstan-var \SplFixedArray<Item|null>
|
||||
*/
|
||||
protected \SplFixedArray $slots;
|
||||
|
||||
public function __construct(int $size){
|
||||
$this->slots = new \SplFixedArray($size);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the inventory.
|
||||
*/
|
||||
public function getSize() : int{
|
||||
return $this->slots->getSize();
|
||||
}
|
||||
|
||||
public function getItem(int $index) : Item{
|
||||
return $this->slots[$index] !== null ? clone $this->slots[$index] : ItemFactory::air();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getContents(bool $includeEmpty = false) : array{
|
||||
$contents = [];
|
||||
|
||||
foreach($this->slots as $i => $slot){
|
||||
if($slot !== null){
|
||||
$contents[$i] = clone $slot;
|
||||
}elseif($includeEmpty){
|
||||
$contents[$i] = ItemFactory::air();
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
protected function internalSetContents(array $items) : void{
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
if(!isset($items[$i])){
|
||||
$this->clear($i);
|
||||
}else{
|
||||
$this->setItem($i, $items[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function internalSetItem(int $index, Item $item) : void{
|
||||
$this->slots[$index] = $item->isNull() ? null : $item;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user