BaseInventory: added getMatchingItemCount() helper

this eliminates the performance issues described by #5719.
closes #5719

we may want to consider exposing a public API for this in the future, since it might be useful for plugins.
This commit is contained in:
Dylan K. Taylor 2023-04-27 21:08:35 +01:00
parent 4228880509
commit eb136e60c8
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 54 additions and 34 deletions

View File

@ -108,13 +108,23 @@ abstract class BaseInventory implements Inventory{
$this->onContentChange($oldContents);
}
/**
* Helper for utility functions which search the inventory.
* TODO: make this abstract instead of providing a slow default implementation (BC break)
*/
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
$item = $this->getItem($slot);
return $item->equals($test, $checkDamage, $checkTags) ? $item->getCount() : 0;
}
public function contains(Item $item) : bool{
$count = max(1, $item->getCount());
$checkDamage = !$item->hasAnyDamageValue();
$checkTags = $item->hasNamedTag();
foreach($this->getContents() as $i){
if($item->equals($i, $checkDamage, $checkTags)){
$count -= $i->getCount();
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
$slotCount = $this->getMatchingItemCount($i, $item, $checkDamage, $checkTags);
if($slotCount > 0){
$count -= $slotCount;
if($count <= 0){
return true;
}
@ -128,9 +138,9 @@ abstract class BaseInventory implements Inventory{
$slots = [];
$checkDamage = !$item->hasAnyDamageValue();
$checkTags = $item->hasNamedTag();
foreach($this->getContents() as $index => $i){
if($item->equals($i, $checkDamage, $checkTags)){
$slots[$index] = $i;
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
if($this->getMatchingItemCount($i, $item, $checkDamage, $checkTags) > 0){
$slots[$i] = $this->getItem($i);
}
}
@ -142,9 +152,10 @@ abstract class BaseInventory implements Inventory{
$checkDamage = $exact || !$item->hasAnyDamageValue();
$checkTags = $exact || $item->hasNamedTag();
foreach($this->getContents() as $index => $i){
if($item->equals($i, $checkDamage, $checkTags) && ($i->getCount() === $count || (!$exact && $i->getCount() > $count))){
return $index;
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
$slotCount = $this->getMatchingItemCount($i, $item, $checkDamage, $checkTags);
if($slotCount > 0 && ($slotCount === $count || (!$exact && $slotCount > $count))){
return $i;
}
}
@ -177,11 +188,9 @@ abstract class BaseInventory implements Inventory{
if($this->isSlotEmpty($i)){
$count -= $maxStackSize;
}else{
$slot = $this->getItem($i);
if($item->canStackWith($slot)){
if(($diff = $maxStackSize - $slot->getCount()) > 0){
$count -= $diff;
}
$slotCount = $this->getMatchingItemCount($i, $item, true, true);
if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
$count -= $diff;
}
}
@ -226,12 +235,16 @@ abstract class BaseInventory implements Inventory{
$emptySlots[] = $i;
continue;
}
$slotItem = $this->getItem($i);
$slotCount = $this->getMatchingItemCount($i, $newItem, true, true);
if($slotCount === 0){
continue;
}
if($newItem->canStackWith($slotItem) && $slotItem->getCount() < $maxStackSize){
$amount = min($maxStackSize - $slotItem->getCount(), $newItem->getCount());
if($slotCount < $maxStackSize){
$amount = min($maxStackSize - $slotCount, $newItem->getCount());
if($amount > 0){
$newItem->setCount($newItem->getCount() - $amount);
$slotItem = $this->getItem($i);
$slotItem->setCount($slotItem->getCount() + $amount);
$this->setItem($i, $slotItem);
if($newItem->getCount() <= 0){
@ -261,20 +274,20 @@ abstract class BaseInventory implements Inventory{
$checkDamage = !$item->hasAnyDamageValue();
$checkTags = $item->hasNamedTag();
foreach($this->getContents() as $index => $i){
if($item->equals($i, $checkDamage, $checkTags)){
$this->clear($index);
for($i = 0, $size = $this->getSize(); $i < $size; $i++){
if($this->getMatchingItemCount($i, $item, $checkDamage, $checkTags) > 0){
$this->clear($i);
}
}
}
public function removeItem(Item ...$slots) : array{
/** @var Item[] $itemSlots */
/** @var Item[] $searchItems */
/** @var Item[] $slots */
$itemSlots = [];
$searchItems = [];
foreach($slots as $slot){
if(!$slot->isNull()){
$itemSlots[] = clone $slot;
$searchItems[] = clone $slot;
}
}
@ -282,26 +295,28 @@ abstract class BaseInventory implements Inventory{
if($this->isSlotEmpty($i)){
continue;
}
$item = $this->getItem($i);
foreach($itemSlots as $index => $slot){
if($slot->equals($item, !$slot->hasAnyDamageValue(), $slot->hasNamedTag())){
$amount = min($item->getCount(), $slot->getCount());
$slot->setCount($slot->getCount() - $amount);
$item->setCount($item->getCount() - $amount);
$this->setItem($i, $item);
if($slot->getCount() <= 0){
unset($itemSlots[$index]);
foreach($searchItems as $index => $search){
$slotCount = $this->getMatchingItemCount($i, $search, !$search->hasAnyDamageValue(), $search->hasNamedTag());
if($slotCount > 0){
$amount = min($slotCount, $search->getCount());
$search->setCount($search->getCount() - $amount);
$slotItem = $this->getItem($i);
$slotItem->setCount($slotItem->getCount() - $amount);
$this->setItem($i, $slotItem);
if($search->getCount() <= 0){
unset($searchItems[$index]);
}
}
}
if(count($itemSlots) === 0){
if(count($searchItems) === 0){
break;
}
}
return $itemSlots;
return $searchItems;
}
public function clear(int $index) : void{

View File

@ -84,6 +84,11 @@ class SimpleInventory extends BaseInventory{
}
}
protected function getMatchingItemCount(int $slot, Item $test, bool $checkDamage, bool $checkTags) : int{
$slotItem = $this->slots[$slot];
return $slotItem !== null && $slotItem->equals($test, $checkDamage, $checkTags) ? $slotItem->getCount() : 0;
}
public function isSlotEmpty(int $index) : bool{
return $this->slots[$index] === null || $this->slots[$index]->isNull();
}