43    protected int $maxStackSize = Inventory::MAX_STACK;
 
   48    protected array $viewers = [];
 
   57    public function __construct(){
 
   63        return $this->maxStackSize;
 
 
   67        $this->maxStackSize = $size;
 
 
   70    abstract protected function internalSetItem(
int $index, 
Item $item) : void;
 
   74            $item = VanillaItems::AIR();
 
   79        $oldItem = $this->getItem($index);
 
   81        $this->internalSetItem($index, $item);
 
   82        $this->onSlotChange($index, $oldItem);
 
 
   96        Utils::validateArrayValueType($items, function(
Item $item) : void{});
 
   97        if(count($items) > $this->getSize()){
 
   98            $items = array_slice($items, 0, $this->getSize(), 
true);
 
  101        $oldContents = $this->getContents(
true);
 
  103        $listeners = $this->listeners->
toArray();
 
  104        $this->listeners->clear();
 
  105        $viewers = $this->viewers;
 
  108        $this->internalSetContents($items);
 
  110        $this->listeners->add(...$listeners); 
 
  111        foreach($viewers as $id => $viewer){
 
  112            $this->viewers[$id] = $viewer;
 
  115        $this->onContentChange($oldContents);
 
 
  123        $item = $this->getItem($slot);
 
  124        return $item->equals($test, 
true, $checkTags) ? $item->getCount() : 0;
 
 
  128        $count = max(1, $item->getCount());
 
  130        for($i = 0, $size = $this->getSize(); $i < $size; $i++){
 
  131            $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
 
  133                $count -= $slotCount;
 
 
  146        for($i = 0, $size = $this->getSize(); $i < $size; $i++){
 
  147            if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
 
  148                $slots[$i] = $this->getItem($i);
 
 
  155    public function first(
Item $item, 
bool $exact = 
false) : int{
 
  156        $count = $exact ? $item->getCount() : max(1, $item->getCount());
 
  159        for($i = 0, $size = $this->getSize(); $i < $size; $i++){
 
  160            $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
 
  161            if($slotCount > 0 && ($slotCount === $count || (!$exact && $slotCount > $count))){
 
 
  170        for($i = 0, $size = $this->getSize(); $i < $size; $i++){
 
  171            if($this->isSlotEmpty($i)){
 
 
  184        return $this->getItem($index)->isNull();
 
 
  188        return $this->getAddableItemQuantity($item) === $item->getCount();
 
 
  192        $count = $item->getCount();
 
  193        $maxStackSize = min($this->getMaxStackSize(), $item->
getMaxStackSize());
 
  195        for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
 
  196            if($this->isSlotEmpty($i)){
 
  197                $count -= $maxStackSize;
 
  199                $slotCount = $this->getMatchingItemCount($i, $item, 
true);
 
  200                if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
 
  206                return $item->getCount();
 
  210        return $item->getCount() - $count;
 
 
  217        foreach($slots as $slot){
 
  218            if(!$slot->isNull()){
 
  219                $itemSlots[] = clone $slot;
 
  226        foreach($itemSlots as $item){
 
  227            $leftover = $this->internalAddItem($item);
 
  228            if(!$leftover->isNull()){
 
  229                $returnSlots[] = $leftover;
 
 
  236    private function internalAddItem(Item $newItem) : Item{
 
  239        $maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
 
  241        for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
 
  242            if($this->isSlotEmpty($i)){
 
  246            $slotCount = $this->getMatchingItemCount($i, $newItem, 
true);
 
  247            if($slotCount === 0){
 
  251            if($slotCount < $maxStackSize){
 
  252                $amount = min($maxStackSize - $slotCount, $newItem->getCount());
 
  254                    $newItem->setCount($newItem->getCount() - $amount);
 
  255                    $slotItem = $this->getItem($i);
 
  256                    $slotItem->setCount($slotItem->getCount() + $amount);
 
  257                    $this->setItem($i, $slotItem);
 
  258                    if($newItem->getCount() <= 0){
 
  265        if(count($emptySlots) > 0){
 
  266            foreach($emptySlots as $slotIndex){
 
  267                $amount = min($maxStackSize, $newItem->getCount());
 
  268                $newItem->setCount($newItem->getCount() - $amount);
 
  269                $slotItem = clone $newItem;
 
  270                $slotItem->setCount($amount);
 
  271                $this->setItem($slotIndex, $slotItem);
 
  272                if($newItem->getCount() <= 0){
 
  281    public function remove(
Item $item) : void{
 
  282        $checkTags = $item->hasNamedTag();
 
  284        for($i = 0, $size = $this->getSize(); $i < $size; $i++){
 
  285            if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
 
 
  293        foreach($slots as $slot){
 
  294            if(!$slot->isNull()){
 
  295                $searchItems[] = clone $slot;
 
  299        for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
 
  300            if($this->isSlotEmpty($i)){
 
  304            foreach($searchItems as $index => $search){
 
  305                $slotCount = $this->getMatchingItemCount($i, $search, $search->hasNamedTag());
 
  307                    $amount = min($slotCount, $search->getCount());
 
  308                    $search->setCount($search->getCount() - $amount);
 
  310                    $slotItem = $this->getItem($i);
 
  311                    $slotItem->setCount($slotItem->getCount() - $amount);
 
  312                    $this->setItem($i, $slotItem);
 
  313                    if($search->getCount() <= 0){
 
  314                        unset($searchItems[$index]);
 
  319            if(count($searchItems) === 0){
 
 
  327    public function clear(
int $index) : void{
 
 
  332        $this->setContents([]);
 
 
  335    public function swap(
int $slot1, 
int $slot2) : void{
 
  336        $i1 = $this->getItem($slot1);
 
  337        $i2 = $this->getItem($slot2);
 
  338        $this->setItem($slot1, $i2);
 
  339        $this->setItem($slot2, $i1);
 
 
  346        return $this->viewers;
 
 
  353        foreach($this->viewers as $hash => $viewer){
 
  354            if($viewer->getCurrentWindow() === $this){ 
 
  355                $viewer->removeCurrentWindow();
 
  357            unset($this->viewers[$hash]);
 
 
  362        $this->viewers[spl_object_id($who)] = $who;
 
 
  365    public function onClose(
Player $who) : void{
 
  366        unset($this->viewers[spl_object_id($who)]);
 
  369    protected function onSlotChange(
int $index, Item $before) : void{
 
  370        foreach($this->listeners as $listener){
 
  371            $listener->onSlotChange($this, $index, $before);
 
  373        foreach($this->viewers as $viewer){
 
  374            $invManager = $viewer->getNetworkSession()->getInvManager();
 
  375            if($invManager === 
null){
 
  378            $invManager->onSlotChange($this, $index);
 
  387        foreach($this->listeners as $listener){
 
  388            $listener->onContentChange($this, $itemsBefore);
 
  391        foreach($this->getViewers() as $viewer){
 
  392            $invManager = $viewer->getNetworkSession()->getInvManager();
 
  393            if($invManager === 
null){
 
  396            $invManager->syncContents($this);
 
 
  401        return $slot >= 0 && $slot < $this->getSize();
 
 
  405        return $this->listeners;
 
 
  409        return $this->validators;