22declare(strict_types=1);
31use
function array_slice;
35use
function spl_object_id;
41 protected int $maxStackSize = Inventory::MAX_STACK;
43 protected array $viewers = [];
50 public function __construct(){
55 return $this->maxStackSize;
59 $this->maxStackSize = $size;
62 abstract protected function internalSetItem(
int $index,
Item $item) : void;
66 $item = VanillaItems::AIR();
71 $oldItem = $this->getItem($index);
73 $this->internalSetItem($index, $item);
74 $this->onSlotChange($index, $oldItem);
88 Utils::validateArrayValueType($items, function(
Item $item) : void{});
89 if(count($items) > $this->getSize()){
90 $items = array_slice($items, 0, $this->getSize(),
true);
93 $oldContents = $this->getContents(
true);
95 $listeners = $this->listeners->
toArray();
96 $this->listeners->clear();
97 $viewers = $this->viewers;
100 $this->internalSetContents($items);
102 $this->listeners->add(...$listeners);
103 foreach($viewers as $id => $viewer){
104 $this->viewers[$id] = $viewer;
107 $this->onContentChange($oldContents);
115 $item = $this->getItem($slot);
116 return $item->equals($test,
true, $checkTags) ? $item->getCount() : 0;
120 $count = max(1, $item->getCount());
122 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
123 $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
125 $count -= $slotCount;
138 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
139 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
140 $slots[$i] = $this->getItem($i);
147 public function first(
Item $item,
bool $exact =
false) : int{
148 $count = $exact ? $item->getCount() : max(1, $item->getCount());
151 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
152 $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
153 if($slotCount > 0 && ($slotCount === $count || (!$exact && $slotCount > $count))){
162 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
163 if($this->isSlotEmpty($i)){
176 return $this->getItem($index)->isNull();
180 return $this->getAddableItemQuantity($item) === $item->getCount();
184 $count = $item->getCount();
185 $maxStackSize = min($this->getMaxStackSize(), $item->
getMaxStackSize());
187 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
188 if($this->isSlotEmpty($i)){
189 $count -= $maxStackSize;
191 $slotCount = $this->getMatchingItemCount($i, $item,
true);
192 if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
198 return $item->getCount();
202 return $item->getCount() - $count;
209 foreach($slots as $slot){
210 if(!$slot->isNull()){
211 $itemSlots[] = clone $slot;
218 foreach($itemSlots as $item){
219 $leftover = $this->internalAddItem($item);
220 if(!$leftover->isNull()){
221 $returnSlots[] = $leftover;
228 private function internalAddItem(Item $newItem) : Item{
231 $maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
233 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
234 if($this->isSlotEmpty($i)){
238 $slotCount = $this->getMatchingItemCount($i, $newItem,
true);
239 if($slotCount === 0){
243 if($slotCount < $maxStackSize){
244 $amount = min($maxStackSize - $slotCount, $newItem->getCount());
246 $newItem->setCount($newItem->getCount() - $amount);
247 $slotItem = $this->getItem($i);
248 $slotItem->setCount($slotItem->getCount() + $amount);
249 $this->setItem($i, $slotItem);
250 if($newItem->getCount() <= 0){
257 if(count($emptySlots) > 0){
258 foreach($emptySlots as $slotIndex){
259 $amount = min($maxStackSize, $newItem->getCount());
260 $newItem->setCount($newItem->getCount() - $amount);
261 $slotItem = clone $newItem;
262 $slotItem->setCount($amount);
263 $this->setItem($slotIndex, $slotItem);
264 if($newItem->getCount() <= 0){
273 public function remove(
Item $item) :
void{
274 $checkTags = $item->hasNamedTag();
276 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
277 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
287 foreach($slots as $slot){
288 if(!$slot->isNull()){
289 $searchItems[] = clone $slot;
293 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
294 if($this->isSlotEmpty($i)){
298 foreach($searchItems as $index => $search){
299 $slotCount = $this->getMatchingItemCount($i, $search, $search->hasNamedTag());
301 $amount = min($slotCount, $search->getCount());
302 $search->setCount($search->getCount() - $amount);
304 $slotItem = $this->getItem($i);
305 $slotItem->setCount($slotItem->getCount() - $amount);
306 $this->setItem($i, $slotItem);
307 if($search->getCount() <= 0){
308 unset($searchItems[$index]);
313 if(count($searchItems) === 0){
321 public function clear(
int $index) : void{
326 $this->setContents([]);
329 public function swap(
int $slot1,
int $slot2) : void{
330 $i1 = $this->getItem($slot1);
331 $i2 = $this->getItem($slot2);
332 $this->setItem($slot1, $i2);
333 $this->setItem($slot2, $i1);
340 return $this->viewers;
347 foreach($this->viewers as $hash => $viewer){
348 if($viewer->getCurrentWindow() === $this){
349 $viewer->removeCurrentWindow();
351 unset($this->viewers[$hash]);
356 $this->viewers[spl_object_id($who)] = $who;
359 public function onClose(
Player $who) : void{
360 unset($this->viewers[spl_object_id($who)]);
363 protected function onSlotChange(
int $index, Item $before) : void{
364 foreach($this->listeners as $listener){
365 $listener->onSlotChange($this, $index, $before);
367 foreach($this->viewers as $viewer){
368 $invManager = $viewer->getNetworkSession()->getInvManager();
369 if($invManager ===
null){
372 $invManager->onSlotChange($this, $index);
381 foreach($this->listeners as $listener){
382 $listener->onContentChange($this, $itemsBefore);
385 foreach($this->getViewers() as $viewer){
386 $invManager = $viewer->getNetworkSession()->getInvManager();
387 if($invManager ===
null){
390 $invManager->syncContents($this);
395 return $slot >= 0 && $slot < $this->getSize();
399 return $this->listeners;
first(Item $item, bool $exact=false)
setContents(array $items)
getMatchingItemCount(int $slot, Item $test, bool $checkTags)
removeItem(Item ... $slots)
internalSetContents(array $items)
getAddableItemQuantity(Item $item)
setMaxStackSize(int $size)
onContentChange(array $itemsBefore)
setItem(int $index, Item $item)
swap(int $slot1, int $slot2)