From 6fcd5322d058dfab5845c61a6443cb837396831d Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Tue, 27 May 2014 01:20:18 +0200 Subject: [PATCH] Implemented Transactions --- src/pocketmine/Player.php | 284 ++++-------------- src/pocketmine/inventory/BaseInventory.php | 3 +- src/pocketmine/inventory/BaseTransaction.php | 64 ++++ .../inventory/CraftingInventory.php | 60 ++++ src/pocketmine/inventory/CraftingManager.php | 73 +++++ .../inventory/CraftingTransactionGroup.php | 36 +++ src/pocketmine/inventory/FurnaceRecipe.php | 114 +++++++ src/pocketmine/inventory/Recipe.php | 32 ++ src/pocketmine/inventory/ShapedRecipe.php | 101 +++++++ src/pocketmine/inventory/ShapelessRecipe.php | 92 ++++++ .../inventory/SimpleTransactionGroup.php | 109 +++++++ src/pocketmine/inventory/Transaction.php | 47 +++ src/pocketmine/inventory/TransactionGroup.php | 63 ++++ 13 files changed, 844 insertions(+), 234 deletions(-) create mode 100644 src/pocketmine/inventory/BaseTransaction.php create mode 100644 src/pocketmine/inventory/CraftingInventory.php create mode 100644 src/pocketmine/inventory/CraftingManager.php create mode 100644 src/pocketmine/inventory/CraftingTransactionGroup.php create mode 100644 src/pocketmine/inventory/FurnaceRecipe.php create mode 100644 src/pocketmine/inventory/Recipe.php create mode 100644 src/pocketmine/inventory/ShapedRecipe.php create mode 100644 src/pocketmine/inventory/ShapelessRecipe.php create mode 100644 src/pocketmine/inventory/SimpleTransactionGroup.php create mode 100644 src/pocketmine/inventory/Transaction.php create mode 100644 src/pocketmine/inventory/TransactionGroup.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 381f54e42..decaefa3a 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -37,8 +37,12 @@ use pocketmine\event\player\PlayerQuitEvent; use pocketmine\event\player\PlayerRespawnEvent; use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketSendEvent; +use pocketmine\inventory\BaseTransaction; +use pocketmine\inventory\CraftingTransactionGroup; +use pocketmine\inventory\FurnaceInventory; use pocketmine\inventory\Inventory; use pocketmine\inventory\InventoryHolder; +use pocketmine\inventory\SimpleTransactionGroup; use pocketmine\item\Item; use pocketmine\level\format\pmf\LevelFormat; use pocketmine\level\Level; @@ -110,9 +114,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ public $achievements = []; public $chunksLoaded = []; public $lastCorrect; - public $craftingItems = []; - public $toCraft = []; - public $lastCraft = 0; + /** @var SimpleTransactionGroup */ + public $currentTransaction = null; + protected $isCrafting = false; public $loginData = []; protected $lastMovement = 0; protected $forceMovement = false; @@ -1570,8 +1574,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->dataPacket($pk); break; } - $this->craftingItems = []; - $this->toCraft = []; + $packet->eid = $this->id; if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place @@ -1632,8 +1635,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ break; } $packet->eid = $this->id; - $this->craftingItems = []; - $this->toCraft = []; switch($packet->action){ case 5: //Shot arrow @@ -1707,8 +1708,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->blocked === true){ break; } - $this->craftingItems = []; - $this->toCraft = []; $vector = new Vector3($packet->x, $packet->y, $packet->z); @@ -1742,8 +1741,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->blocked === true){ break; } - $this->craftingItems = []; - $this->toCraft = []; for($i = 0; $i < 4; ++$i){ $s = $packet->slots[$i]; @@ -1784,8 +1781,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $data["target"] = $packet->target; $data["eid"] = $packet->eid; $data["action"] = $packet->action; - $this->craftingItems = []; - $this->toCraft = []; $target = Entity::get($packet->target); if($target instanceof Entity and $this->gamemode !== VIEW and $this->blocked === false and ($target instanceof Entity) and $this->entity->distance($target) <= 8){ $data["targetentity"] = $target; @@ -1875,8 +1870,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->dead === false){ break; } - $this->craftingItems = []; - $this->toCraft = []; $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->spawnPosition)); @@ -1898,8 +1891,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->blocked === true){ break; } - $this->craftingItems = []; - $this->toCraft = []; + $packet->eid = $this->id; if($this->entity->inAction === true){ $this->entity->inAction = false; @@ -1954,8 +1946,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } $packet->eid = $this->id; $packet->item = $this->getSlot($this->getCurrentEquipment()); - $this->craftingItems = []; - $this->toCraft = []; $data = []; $data["eid"] = $packet->eid; $data["unknown"] = $packet->unknown; @@ -1974,8 +1964,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false){ break; } - $this->craftingItems = []; - $this->toCraft = []; $packet->message = TextFormat::clean($packet->message); if(trim($packet->message) != "" and strlen($packet->message) <= 255){ $message = $packet->message; @@ -1997,8 +1985,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $packet->windowid === 0){ break; } - $this->craftingItems = []; - $this->toCraft = []; if(isset($this->windowIndex[$packet->windowid])){ $this->removeWindow($this->windowIndex[$packet->windowid]); }else{ @@ -2009,121 +1995,61 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->blocked === true){ break; } - /** @var $packet \pocketmine\network\protocol\ContainerSetContentPacket */ console("[TRANSACTION] {$this->username} {$packet->windowid}[ ".$packet->item->getID().":".$packet->item->getDamage()."(".$packet->item->getCount().") -> #{$packet->slot} ]"); - - if($this->lastCraft <= (microtime(true) - 1)){ - if(isset($this->toCraft[-1])){ - $this->toCraft = array(-1 => $this->toCraft[-1]); - }else{ - $this->toCraft = []; - } - $this->craftingItems = []; + if($this->currentTransaction === null or $this->currentTransaction->getCreationTime() > (microtime(true) - 1)){ + $this->currentTransaction = new SimpleTransactionGroup(); } - if($packet->windowid == 0){ //Crafting! - //TODO: crafting event - - - $craft = false; - $slot = $this->inventory->getItem($packet->slot); - if($slot->getCount() >= $packet->item->getCount() and (($slot->getID() === $packet->item->getID() and $slot->getDamage() === $packet->item->getDamage()) or ($packet->item->getID() === Item::AIR and $packet->item->getCount() === 0)) and !isset($this->craftingItems[$packet->slot])){ //Crafting recipe - $use = clone $slot; - $use->setCount($slot->getCount() - $packet->item->getCount()); - $this->craftingItems[$packet->slot] = $use; - $craft = true; - }elseif($slot->getCount() <= $packet->item->getCount() and ($slot->getID() === Item::AIR or ($slot->getID() === $packet->item->getID() and $slot->getDamage() === $packet->item->getDamage()))){ //Crafting final - /** @var Item $craftItem */ - $craftItem = clone $packet->item; - $craftItem->setCount($packet->item->getCount() - $slot->getCount()); - if(count($this->toCraft) === 0){ - $this->toCraft[-1] = 0; - } - $this->toCraft[$packet->slot] = $craftItem; - $craft = true; - }elseif(((count($this->toCraft) === 1 and isset($this->toCraft[-1])) or count($this->toCraft) === 0) and $slot->getCount() > 0 and $slot->getID() > Item::AIR and ($slot->getID() !== $packet->item->getID() or $slot->getDamage() !== $packet->item->getDamage())){ //Crafting final - $craftItem = Item::get($packet->item->getID(), $packet->item->getDamage(), $packet->item->getCount()); - if(count($this->toCraft) === 0){ - $this->toCraft[-1] = 0; - } - $use = clone $slot; - $this->craftingItems[$packet->slot] = $use; - $this->toCraft[$packet->slot] = $craftItem; - $craft = true; - } - - if($craft === true){ - $this->lastCraft = microtime(true); - } - - if($craft === true and count($this->craftingItems) > 0 and count($this->toCraft) > 0 and ($recipe = $this->craftItems($this->toCraft, $this->craftingItems, $this->toCraft[-1])) !== true){ - if($recipe === false){ - $this->inventory->sendContents($this); - $this->toCraft = []; - }else{ - $this->toCraft = array(-1 => $this->toCraft[-1]); - } - $this->craftingItems = []; - } - break; - }else{ - $this->toCraft = []; - $this->craftingItems = []; - } - if(!isset($this->windowIndex[$packet->windowid])){ + if($packet->slot < 0){ break; } - $inv = $this->windowIndex[$packet->windowid]; - if($packet->slot < 0 or $packet->slot >= $inv->getSize()){ - break; - } - - /** @var Item $item */ - $item = clone $packet->item; - - $slot = $inv->getItem($packet->slot); - - if($item->getID() !== Item::AIR and $slot->equals($item, true)){ - if($slot->getCount() < $item->getCount()){ - $it = clone $item; - $it->setCount($item->getCount() - $slot->getCount()); - if(!$this->inventory->contains($it)){ - $this->inventory->sendContents($this); - $inv->sendContents($this); - break; - } - $this->inventory->removeItem($it); - }elseif($slot->getCount() > $item->getCount()){ - $it = clone $item; - $it->setCount($slot->getCount() - $item->getCount()); - if(!$this->inventory->canAddItem($it)){ - $this->inventory->sendContents($this); - $inv->sendContents($this); - break; - } - $this->inventory->addItem($it); - } - }else{ //same slot replace - if(!$this->inventory->contains($item)){ - $this->inventory->sendContents($this); - $inv->sendContents($this); + if($packet->windowid === 0){ //Our inventory + if($packet->slot > $this->inventory->getSize()){ break; } - $this->inventory->removeItem($item); - $this->inventory->addItem($slot); + $transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item); + }elseif(isset($this->windowIndex[$packet->windowid])){ + $inv = $this->windowIndex[$packet->windowid]; + $transaction = new BaseTransaction($inv, $packet->slot, $inv->getItem($packet->slot), $packet->item); + }else{ + break; } - if($inv->getType()->getDefaultTitle() === "Furnace" and $packet->slot === 2){ - switch($slot->getID()){ - case Item::IRON_INGOT: - $this->awardAchievement("acquireIron"); - break; + $this->currentTransaction->addTransaction($transaction); + + if($this->currentTransaction->canExecute()){ + foreach($this->currentTransaction->getTransactions() as $ts){ + $inv = $ts->getInventory(); + if($inv instanceof FurnaceInventory){ + if($ts->getSlot() === 2){ + switch($inv->getResult()){ + case Item::IRON_INGOT: + $this->awardAchievement("acquireIron"); + break; + } + } + } + + $this->currentTransaction = null; } + }elseif($packet->windowid == 0){ //Try crafting + $craftingGroup = new CraftingTransactionGroup($this->currentTransaction); + if($craftingGroup->canExecute()){ //We can craft! + //TODO: CraftItemEvent + //$this->server->getPluginManager($ev = new CraftItemEvent($this, $recipe, $craft, $type)); + //if($ev->isCancelled()){ + // return false; + //} + + $craftingGroup->execute(); + } + + $this->currentTransaction = null; } - $inv->setItem($packet->slot, $item); + break; case ProtocolInfo::SEND_INVENTORY_PACKET: //TODO, Mojang, enable this ยด^_^` @@ -2135,8 +2061,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($this->spawned === false or $this->blocked === true){ break; } - $this->craftingItems = []; - $this->toCraft = []; + $t = $this->getLevel()->getTile(new Vector3($packet->x, $packet->y, $packet->z)); if($t instanceof Sign){ if($t->namedtag->creator !== $this->username){ @@ -2253,7 +2178,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->chunksLoaded = []; $this->chunksOrder = []; $this->chunkCount = []; - $this->craftingItems = []; $this->received = []; $this->buffer = null; unset($this->buffer); @@ -2325,112 +2249,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ return $this->username; } - /** - * @param Item[] $craft - * @param Item[] $recipe - * @param int $type - * - * @return array|bool - */ - public function craftItems(array $craft, array $recipe, $type){ - $craftItem = array(0, true, 0); - unset($craft[-1]); - foreach($craft as $item){ - if($item instanceof Item){ - $craftItem[0] = $item->getID(); - if($item->getDamage() !== $craftItem[1] and $craftItem[1] !== true){ - $craftItem[1] = false; - }else{ - $craftItem[1] = $item->getDamage(); - } - $craftItem[2] += $item->getCount(); - } - - } - - $recipeItems = []; - foreach($recipe as $item){ - if(!isset($recipeItems[$item->getID()])){ - $recipeItems[$item->getID()] = array($item->getID(), $item->getDamage(), $item->getCount()); - }else{ - if($item->getDamage() !== $recipeItems[$item->getID()][1]){ - $recipeItems[$item->getID()][1] = false; - } - $recipeItems[$item->getID()][2] += $item->getCount(); - } - } - - $res = Crafting::canCraft($craftItem, $recipeItems, $type); - - if(!is_array($res) and $type === 1){ - $res2 = Crafting::canCraft($craftItem, $recipeItems, 0); - if(is_array($res2)){ - $res = $res2; - } - } - - if(is_array($res)){ - //TODO: CraftItemEvent - //$this->server->getPluginManager($ev = new CraftItemEvent($this, $recipe, $craft, $type)); - //if($ev->isCancelled()){ - // return false; - //} - - foreach($recipe as $slot => $item){ - $s = $this->inventory->getItem($slot); - $s->setCount($s->getCount() - $item->getCount()); - if($s->getCount() <= 0){ - $this->inventory->setItem($slot, Item::get(Item::AIR, 0, 0)); - } - } - foreach($craft as $slot => $item){ - $s = $this->inventory->getItem($slot); - if($s->getCount() <= 0 or $s->getID() === Item::AIR){ - $this->inventory->setItem($slot, Item::get($item->getID(), $item->getDamage(), $item->getCount())); - }else{ - $this->inventory->setItem($slot, Item::get($item->getID(), $item->getDamage(), $s->getCount() + $item->getCount())); - } - - switch($item->getID()){ - case Item::WORKBENCH: - $this->awardAchievement("buildWorkBench"); - break; - case Item::WOODEN_PICKAXE: - $this->awardAchievement("buildPickaxe"); - break; - case Item::FURNACE: - $this->awardAchievement("buildFurnace"); - break; - case Item::WOODEN_HOE: - $this->awardAchievement("buildHoe"); - break; - case Item::BREAD: - $this->awardAchievement("makeBread"); - break; - case Item::CAKE: - $this->awardAchievement("bakeCake"); - $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); - break; - case Item::STONE_PICKAXE: - case Item::GOLD_PICKAXE: - case Item::IRON_PICKAXE: - case Item::DIAMOND_PICKAXE: - $this->awardAchievement("buildBetterPickaxe"); - break; - case Item::WOODEN_SWORD: - $this->awardAchievement("buildSword"); - break; - case Item::DIAMOND: - $this->awardAchievement("diamond"); - break; - - } - } - } - - return $res; - } - /** * @param Inventory $inventory diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index 9408b9b13..dba3eb283 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -94,7 +94,7 @@ abstract class BaseInventory implements Inventory{ } public function getItem($index){ - return isset($this->slots[$index]) ? $this->slots[$index] : Item::get(Item::AIR, null, 0); + return isset($this->slots[$index]) ? clone $this->slots[$index] : Item::get(Item::AIR, null, 0); } public function getContents(){ @@ -121,6 +121,7 @@ abstract class BaseInventory implements Inventory{ } public function setItem($index, Item $item){ + $item = clone $item; if($index < 0 or $index >= $this->size){ return false; }elseif($item->getID() === 0){ diff --git a/src/pocketmine/inventory/BaseTransaction.php b/src/pocketmine/inventory/BaseTransaction.php new file mode 100644 index 000000000..e769a4d24 --- /dev/null +++ b/src/pocketmine/inventory/BaseTransaction.php @@ -0,0 +1,64 @@ +inventory = $inventory; + $this->slot = (int) $slot; + $this->sourceItem = clone $sourceItem; + $this->targetItem = clone $targetItem; + } + + public function getInventory(){ + return $this->inventory; + } + + public function getSlot(){ + return $this->slot; + } + + public function getSourceItem(){ + return clone $this->sourceItem; + } + + public function getTargetItem(){ + return clone $this->targetItem; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingInventory.php b/src/pocketmine/inventory/CraftingInventory.php new file mode 100644 index 000000000..2a6d650a6 --- /dev/null +++ b/src/pocketmine/inventory/CraftingInventory.php @@ -0,0 +1,60 @@ +getDefaultTitle() !== "Crafting"){ + throw new \Exception("Invalid Inventory type, expected CRAFTING or WORKBENCH"); + } + $this->resultInventory = $resultInventory; + parent::__construct($holder, $inventoryType); + } + + /** + * @return Inventory + */ + public function getResultInventory(){ + return $this->resultInventory; + } + + public function getSize(){ + return $this->getResultInventory()->getSize() + parent::getSize(); + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php new file mode 100644 index 000000000..9072326e5 --- /dev/null +++ b/src/pocketmine/inventory/CraftingManager.php @@ -0,0 +1,73 @@ +registerShapedRecipe($recipe); + }elseif($recipe instanceof ShapelessRecipe){ + $this->registerShapelessRecipe($recipe); + }elseif($recipe instanceof FurnaceRecipe){ + $this->registerFurnaceRecipe($recipe); + } + } + +} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingTransactionGroup.php b/src/pocketmine/inventory/CraftingTransactionGroup.php new file mode 100644 index 000000000..1083d3b19 --- /dev/null +++ b/src/pocketmine/inventory/CraftingTransactionGroup.php @@ -0,0 +1,36 @@ +transactions = $group->getTransactions(); + $this->inventories = $group->getInventories(); + } + + public function canExecute(){ + //TODO: search in crafting recipes + return false; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/FurnaceRecipe.php b/src/pocketmine/inventory/FurnaceRecipe.php new file mode 100644 index 000000000..c46481f90 --- /dev/null +++ b/src/pocketmine/inventory/FurnaceRecipe.php @@ -0,0 +1,114 @@ +output = clone $result; + $this->ingredient = clone $ingredient; + } + + /** + * @param Item $item + */ + public function setInput(Item $item){ + $this->ingredient = clone $item; + } + + /** + * @return Item + */ + public function getInput(){ + return clone $this->ingredient; + } + + /** + * @return Item + */ + public function getResult(){ + return clone $this->output; + } + + /** + * @param Item $item + * + * @returns ShapelessRecipe + * + * @throws \Exception + */ + public function addIngredient(Item $item){ + if(count($this->ingredients) >= 9){ + throw new \Exception("Shapeless recipes cannot have more than 9 ingredients"); + } + + $it = clone $item; + $it->setCount(1); + + while($item->getCount() > 0){ + $this->ingredients[] = clone $it; + $item->setCount($item->getCount() - 1); + } + + return $this; + } + + /** + * @param Item $item + * + * @return $this + */ + public function removeIngredient(Item $item){ + foreach($this->ingredients as $index => $ingredient){ + if($item->getCount() <= 0){ + break; + } + if($ingredient->equals($item, $item->getDamage() === null ? false : true)){ + unset($this->ingredients[$index]); + $item->setCount($item->getCount() - 1); + } + } + return $this; + } + + /** + * @return Item[] + */ + public function getIngredientList(){ + $ingredients = []; + foreach($this->ingredients as $ingredient){ + $ingredients[] = clone $ingredient; + } + return $ingredients; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/Recipe.php b/src/pocketmine/inventory/Recipe.php new file mode 100644 index 000000000..ae11e250b --- /dev/null +++ b/src/pocketmine/inventory/Recipe.php @@ -0,0 +1,32 @@ + 3){ + throw new \Exception("Crafting recipes should be 1, 2, 3 rows, not ".count($shape)); + } + foreach($shape as $row){ + if(strlen($row) === 0 or strlen($row) > 3){ + throw new \Exception("Crafting rows should be 1, 2, 3 characters, not ".count($row)); + } + $this->rows[] = $row; + $len = strlen($row); + for($i = 0; $i < $len; ++$i){ + $this->ingredients[$row{$i}] = null; + } + } + + $this->output = clone $result; + } + + /** + * @param string $key + * @param Item $item + * + * @return $this + * @throws \Exception + */ + public function setIngredient($key, Item $item){ + if(!isset($this->ingredients[$key])){ + throw new \Exception("Symbol does not appear in the shape: ". $key); + } + + $this->ingredients[$key] = $item; + + return $this; + } + + /** + * @return Item[] + */ + public function getIngredientMap(){ + $ingredients = []; + foreach($this->ingredients as $key => $ingredient){ + if($ingredient instanceof Item){ + $ingredients[$key] = clone $ingredient; + }else{ + $ingredients[$key] = $ingredient; + } + } + return $ingredients; + } + + /** + * @return string[] + */ + public function getShape(){ + return $this->rows; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/ShapelessRecipe.php b/src/pocketmine/inventory/ShapelessRecipe.php new file mode 100644 index 000000000..bd657cf5b --- /dev/null +++ b/src/pocketmine/inventory/ShapelessRecipe.php @@ -0,0 +1,92 @@ +output = clone $result; + } + + public function getResult(){ + return clone $this->output; + } + + /** + * @param Item $item + * + * @returns ShapelessRecipe + * + * @throws \Exception + */ + public function addIngredient(Item $item){ + if(count($this->ingredients) >= 9){ + throw new \Exception("Shapeless recipes cannot have more than 9 ingredients"); + } + + $it = clone $item; + $it->setCount(1); + + while($item->getCount() > 0){ + $this->ingredients[] = clone $it; + $item->setCount($item->getCount() - 1); + } + + return $this; + } + + /** + * @param Item $item + * + * @return $this + */ + public function removeIngredient(Item $item){ + foreach($this->ingredients as $index => $ingredient){ + if($item->getCount() <= 0){ + break; + } + if($ingredient->equals($item, $item->getDamage() === null ? false : true)){ + unset($this->ingredients[$index]); + $item->setCount($item->getCount() - 1); + } + } + return $this; + } + + /** + * @return Item[] + */ + public function getIngredientList(){ + $ingredients = []; + foreach($this->ingredients as $ingredient){ + $ingredients[] = clone $ingredient; + } + return $ingredients; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/SimpleTransactionGroup.php b/src/pocketmine/inventory/SimpleTransactionGroup.php new file mode 100644 index 000000000..390c42c35 --- /dev/null +++ b/src/pocketmine/inventory/SimpleTransactionGroup.php @@ -0,0 +1,109 @@ +creationTime = microtime(true); + } + + public function getCreationTime(){ + return $this->creationTime; + } + + public function getInventories(){ + return $this->inventories; + } + + public function getTransactions(){ + return $this->transactions; + } + + public function addTransaction(Transaction $transaction){ + $this->transactions[spl_object_hash($transaction)] = $transaction; + $this->inventories[spl_object_hash($transaction->getInventory())] = $transaction->getInventory(); + } + + public function canExecute(){ + /** @var Item[] $needItems */ + $needItems = []; + /** @var Item[] $haveItems */ + $haveItems = []; + foreach($this->transactions as $key => $ts){ + $needItems[] = $ts->getTargetItem(); + $checkSourceItem = $ts->getInventory()->getItem($ts->getSlot()); + $sourceItem = $ts->getSourceItem(); + if(!$checkSourceItem->equals($sourceItem, true) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ + return false; + } + $haveItems[] = $sourceItem; + } + + foreach($needItems as $i => $needItem){ + foreach($haveItems as $j => $haveItem){ + if($needItem->equals($haveItem, true)){ + $amount = min($needItem->getCount(), $haveItem->getCount()); + $needItem->setCount($needItem->getCount() - $amount); + $haveItem->setCount($haveItem->getCount() - $amount); + if($haveItem->getCount() === 0){ + unset($haveItems[$j]); + } + if($needItem->getCount() === 0){ + unset($needItems[$i]); + break; + } + } + } + } + + return count($haveItems) === 0 and count($needItems) === 0 and count($this->transactions) > 0; + } + + public function execute(){ + if($this->hasExecuted() or !$this->canExecute()){ + return false; + } + foreach($this->transactions as $transaction){ + $transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem()); + } + + return true; + } + + public function hasExecuted(){ + return $this->hasExecuted; + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/Transaction.php b/src/pocketmine/inventory/Transaction.php new file mode 100644 index 000000000..ca95a5f2a --- /dev/null +++ b/src/pocketmine/inventory/Transaction.php @@ -0,0 +1,47 @@ +