mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-04 08:56:15 +00:00
InventoryTransaction: Added a more robust action flattening mechanism
This now handles the case where there are multiple options which could be taken, and opts for the first result which successfully ties all the actions together. Previously it would be entirely down to chance (ordering) whether the actions would get ordered successfully.
This commit is contained in:
@ -165,65 +165,73 @@ class InventoryTransaction{
|
|||||||
protected function squashDuplicateSlotChanges() : bool{
|
protected function squashDuplicateSlotChanges() : bool{
|
||||||
/** @var SlotChangeAction[][] $slotChanges */
|
/** @var SlotChangeAction[][] $slotChanges */
|
||||||
$slotChanges = [];
|
$slotChanges = [];
|
||||||
|
/** @var Inventory[] $inventories */
|
||||||
|
$inventories = [];
|
||||||
|
/** @var int[] $slots */
|
||||||
|
$slots = [];
|
||||||
|
|
||||||
foreach($this->actions as $key => $action){
|
foreach($this->actions as $key => $action){
|
||||||
if($action instanceof SlotChangeAction){
|
if($action instanceof SlotChangeAction){
|
||||||
$slotChanges[spl_object_hash($action->getInventory()) . "@" . $action->getSlot()][] = $action;
|
$slotChanges[$h = (spl_object_hash($action->getInventory()) . "@" . $action->getSlot())][] = $action;
|
||||||
|
$inventories[$h] = $action->getInventory();
|
||||||
|
$slots[$h] = $action->getSlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($slotChanges as $hash => $list){
|
foreach($slotChanges as $hash => $list){
|
||||||
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
|
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
|
||||||
unset($slotChanges[$hash]);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$originalList = $list;
|
$inventory = $inventories[$hash];
|
||||||
|
$slot = $slots[$hash];
|
||||||
|
$sourceItem = $inventory->getItem($slot);
|
||||||
|
|
||||||
/** @var SlotChangeAction|null $originalAction */
|
$targetItem = $this->findResultItem($sourceItem, $list);
|
||||||
$originalAction = null;
|
if($targetItem === null){
|
||||||
/** @var Item|null $lastTargetItem */
|
MainLogger::getLogger()->debug("Failed to compact " . count($list) . " actions for " . $this->source->getName());
|
||||||
$lastTargetItem = null;
|
|
||||||
|
|
||||||
foreach($list as $i => $action){
|
|
||||||
if($action->isValid($this->source)){
|
|
||||||
$originalAction = $action;
|
|
||||||
$lastTargetItem = $action->getTargetItem();
|
|
||||||
unset($list[$i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($originalAction === null){
|
|
||||||
return false; //Couldn't find any actions that had a source-item matching the current inventory slot
|
|
||||||
}
|
|
||||||
|
|
||||||
do{
|
|
||||||
$sortedThisLoop = 0;
|
|
||||||
foreach($list as $i => $action){
|
|
||||||
$actionSource = $action->getSourceItem();
|
|
||||||
if($actionSource->equalsExact($lastTargetItem)){
|
|
||||||
$lastTargetItem = $action->getTargetItem();
|
|
||||||
unset($list[$i]);
|
|
||||||
$sortedThisLoop++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}while($sortedThisLoop > 0);
|
|
||||||
|
|
||||||
if(count($list) > 0){ //couldn't chain all the actions together
|
|
||||||
MainLogger::getLogger()->debug("Failed to compact " . count($originalList) . " actions for " . $this->source->getName());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($originalList as $action){
|
foreach($list as $action){
|
||||||
unset($this->actions[spl_object_hash($action)]);
|
unset($this->actions[spl_object_hash($action)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->addAction(new SlotChangeAction($originalAction->getInventory(), $originalAction->getSlot(), $originalAction->getSourceItem(), $lastTargetItem));
|
if(!$targetItem->equalsExact($sourceItem)){
|
||||||
|
//sometimes we get actions on the crafting grid whose source and target items are the same, so dump them
|
||||||
|
$this->addAction(new SlotChangeAction($inventory, $slot, $sourceItem, $targetItem));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Item $needOrigin
|
||||||
|
* @param SlotChangeAction[] $possibleActions
|
||||||
|
*
|
||||||
|
* @return null|Item
|
||||||
|
*/
|
||||||
|
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||||
|
assert(!empty($possibleActions));
|
||||||
|
|
||||||
|
foreach($possibleActions as $i => $action){
|
||||||
|
if($action->getSourceItem()->equalsExact($needOrigin)){
|
||||||
|
$newList = $possibleActions;
|
||||||
|
unset($newList[$i]);
|
||||||
|
if(empty($newList)){
|
||||||
|
return $action->getTargetItem();
|
||||||
|
}
|
||||||
|
$result = $this->findResultItem($action->getTargetItem(), $newList);
|
||||||
|
if($result !== null){
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user