131    protected function matchItems(array &$needItems, array &$haveItems) : void{
 
  134        foreach($this->actions as $key => $action){
 
  135            $targetItem = $action->getTargetItem();
 
  136            if(!$targetItem->isNull()){
 
  137                $needItems[] = $targetItem;
 
  141                $action->validate($this->source);
 
  146            $sourceItem = $action->getSourceItem();
 
  147            if(!$sourceItem->isNull()){
 
  148                $haveItems[] = $sourceItem;
 
  152        foreach($needItems as $i => $needItem){
 
  153            foreach($haveItems as $j => $haveItem){
 
  154                if($needItem->canStackWith($haveItem)){
 
  155                    $amount = min($needItem->getCount(), $haveItem->getCount());
 
  156                    $needItem->setCount($needItem->getCount() - $amount);
 
  157                    $haveItem->setCount($haveItem->getCount() - $amount);
 
  158                    if($haveItem->getCount() === 0){
 
  159                        unset($haveItems[$j]);
 
  161                    if($needItem->getCount() === 0){
 
  162                        unset($needItems[$i]);
 
  168        $needItems = array_values($needItems);
 
  169        $haveItems = array_values($haveItems);
 
 
  187        foreach($this->actions as $key => $action){
 
  189                $slotChanges[$h = (spl_object_hash($action->getInventory()) . 
"@" . $action->getSlot())][] = $action;
 
  190                $inventories[$h] = $action->getInventory();
 
  191                $slots[$h] = $action->getSlot();
 
  195        foreach(Utils::stringifyKeys($slotChanges) as $hash => $list){
 
  196            if(count($list) === 1){ 
 
  200            $inventory = $inventories[$hash];
 
  201            $slot = $slots[$hash];
 
  202            if(!$inventory->slotExists($slot)){ 
 
  205            $sourceItem = $inventory->getItem($slot);
 
  207            $targetItem = $this->findResultItem($sourceItem, $list);
 
  208            if($targetItem === 
null){
 
  209                throw new TransactionValidationException(
"Failed to compact " . count($list) . 
" duplicate actions");
 
  212            foreach($list as $action){
 
  213                unset($this->actions[spl_object_id($action)]);
 
  216            if(!$targetItem->equalsExact($sourceItem)){
 
  218                $this->addAction(
new SlotChangeAction($inventory, $slot, $sourceItem, $targetItem));
 
 
  228        assert(count($possibleActions) > 0);
 
  231        $newList = $possibleActions;
 
  232        foreach($possibleActions as $i => $action){
 
  233            if($action->getSourceItem()->equalsExact($needOrigin)){
 
  234                if($candidate !== 
null){
 
  245                $candidate = $action;
 
  249        if($candidate === 
null){
 
  254        if(count($newList) === 0){
 
  255            return $candidate->getTargetItem();
 
  257        return $this->findResultItem($candidate->getTargetItem(), $newList);
 
 
  295        if($this->hasExecuted()){
 
  301        if(!$this->callExecuteEvent()){
 
  305        foreach($this->actions as $action){
 
  306            if(!$action->onPreExecute($this->source)){
 
  307                throw new TransactionCancelledException(
"One of the actions in this transaction was cancelled");
 
  311        foreach($this->actions as $action){
 
  312            $action->execute($this->source);
 
  315        $this->hasExecuted = 
true;