From c4c895740dd6c24ae0bc1be194fd69c927a6f1ee Mon Sep 17 00:00:00 2001 From: Muqsit Rayyan Date: Thu, 16 Feb 2017 08:59:39 -0900 Subject: [PATCH] ChestShop v2 UPDATE. * Fixed error when a player quits with the GUI open. * Fixed plugin not detecting item to be purchased. * Fixed unserialize error after adding item to the shop. * NBT::$replace now uses IntArrayTag to save the replaced block's damage. * Added /cs removebyid * Added "default-price" option in config.yml * Added "banned-items" (These items are banned from being /cs add ed). * Fixed many more bugs. --- plugin.yml | 6 +- resources/config.yml | 7 +- src/ChestShop/Chest/CustomChest.php | 5 +- src/ChestShop/Chest/CustomChestInventory.php | 2 +- src/ChestShop/EventListener.php | 23 ++++--- src/ChestShop/Main.php | 67 ++++++++++++++++---- src/ChestShop/RemoveByIdTask.php | 40 ++++++++++++ 7 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 src/ChestShop/RemoveByIdTask.php diff --git a/plugin.yml b/plugin.yml index 7a74f0f..f992768 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,7 @@ name: ChestShop main: ChestShop\Main -api: [2.0.0, 3.0.0] -version: 1.0.0 +api: [2.0.0, 3.0.0, 3.0.0-ALPHA3] +version: 2.0 depend: [EconomyAPI] commands: chestshop: @@ -10,4 +10,4 @@ permissions: chestshop.command.add: default: op chestshop.command.remove: - default: op \ No newline at end of file + default: op diff --git a/resources/config.yml b/resources/config.yml index 880045c..019c408 100644 --- a/resources/config.yml +++ b/resources/config.yml @@ -1,3 +1,6 @@ --- - -... \ No newline at end of file +default-price: 15000 +banned-items: +- 35:1 +- 7 +... diff --git a/src/ChestShop/Chest/CustomChest.php b/src/ChestShop/Chest/CustomChest.php index 03e6b1a..f8a4a08 100644 --- a/src/ChestShop/Chest/CustomChest.php +++ b/src/ChestShop/Chest/CustomChest.php @@ -35,6 +35,7 @@ public function __construct(Chunk $chunk, CompoundTag $nbt){ } public function getReplacement() : Block{ - return Block::get($this->namedtag->replace->getValue() ?? 0); + $replace = $this->namedtag->replace->getValue() ?? [0, 0]; + return Block::get($replace[0], $replace[1]); } -} \ No newline at end of file +} diff --git a/src/ChestShop/Chest/CustomChestInventory.php b/src/ChestShop/Chest/CustomChestInventory.php index 719c0cb..c3b54fa 100644 --- a/src/ChestShop/Chest/CustomChestInventory.php +++ b/src/ChestShop/Chest/CustomChestInventory.php @@ -43,7 +43,7 @@ public function onClose(Player $who){ $block->y = floor($pos->y); $block->z = floor($pos->z); $block->level = $pos->getLevel(); - $block->level->sendBlocks([$who], [$block]); + if($who instanceof Player) $block->level->sendBlocks([$who], [$block]); parent::onClose($who); unset(\ChestShop\Main::getInstance()->clicks[$who->getId()]); $this->holder->close(); diff --git a/src/ChestShop/EventListener.php b/src/ChestShop/EventListener.php index 2c319ef..040c13a 100644 --- a/src/ChestShop/EventListener.php +++ b/src/ChestShop/EventListener.php @@ -34,7 +34,6 @@ class EventListener implements Listener{ - const DEFAULT_PRICE = 15000; protected $plugin; public function __construct(Main $plugin){ @@ -47,7 +46,7 @@ public function __construct(Main $plugin){ * is a Chest Shop tile where the block is. */ public function onBreak(BlockBreakEvent $event){ - $event->setCancelled($event->getBlock()->getLevel()->getTile($event->getBlock()) instanceof CustomChest); + if($event->getBlock()->getLevel()->getTile($event->getBlock()) instanceof CustomChest) $event->setCancelled(); } /** @@ -67,15 +66,19 @@ public function onTransaction(InventoryTransactionEvent $event){ $player = $assumed; $chestinv = $inv; $action = $transaction; - break; + break 2; } } } } - - if($chestinv === null) return; + + /* + * $player => Player interacting with the GUI. + * $chestinv => The chest's inventory. + * $action => BaseTransaction|Transaction|SimpleTransactionGroup + */ $event->setCancelled(); - $item = $action->getTargetItem(); + $item = ($item = $action->getTargetItem())->getId() === 0 ? $action->getSourceItem() : $item; if(isset($item->getNamedTag()->turner)){ $action = $item->getNamedTag()->turner->getValue(); @@ -84,16 +87,18 @@ public function onTransaction(InventoryTransactionEvent $event){ return; } - $data = $item->getNamedTag()->ChestShop->getValue() ?? null; + $data = isset($item->getNamedTag()->ChestShop) ? $item->getNamedTag()->ChestShop->getValue() : null; if($data === null) return; - $price = $data[0] ?? self::DEFAULT_PRICE; + + $price = $data[0] ?? $this->plugin->defaultprice; if(!isset($this->plugin->clicks[$player->getId()][$data[1]])){ $this->plugin->clicks[$player->getId()][$data[1]] = 1; return; } + if(EconomyAPI::getInstance()->myMoney($player) >= $price){ $item = $this->plugin->getItemFromShop($data[1]); - $player->sendMessage(Main::PREFIX.TF::GREEN.'Purchased '.TF::BOLD.$item->getName().TF::RESET.TF::GREEN.TF::GRAY.' (x'.$item->getCount().')'.TF::GREEN.' for $'.$price); + $player->sendMessage(Main::PREFIX.TF::GREEN.'Purchased '.TF::BOLD.$item->getName().TF::RESET.TF::GREEN.TF::GRAY.' (x'.$item->getCount().')'.TF::GREEN.' for $'.$price.'.'); $player->getInventory()->addItem($item); EconomyAPI::getInstance()->reduceMoney($player, $price); unset($this->plugin->clicks[$player->getId()]); diff --git a/src/ChestShop/Main.php b/src/ChestShop/Main.php index ee3aad5..89b6224 100644 --- a/src/ChestShop/Main.php +++ b/src/ChestShop/Main.php @@ -37,10 +37,12 @@ class Main extends PluginBase{ const PREFIX = TF::BOLD.TF::YELLOW.'CS '.TF::RESET; + public $defaultprice; + public $inChestShop, $clicks = []; protected $shops = []; private $helpcmd = []; private static $instance = null; - public $inChestShop, $clicks = []; + private $notallowed = []; public function onEnable(){ self::$instance = $this; @@ -62,7 +64,7 @@ public function onEnable(){ $this->getLogger()->notice(implode("\n", $info)); if(!is_dir($this->getDataFolder())) mkdir($this->getDataFolder()); - foreach(['shops.yml'] as $file){ + foreach(['shops.yml', 'config.yml'] as $file){ if(!is_file($this->getDataFolder().$file)){ $openf = fopen($this->getDataFolder().$file, 'w') or die('Cannot open file: '.$file); file_put_contents($this->getDataFolder().$file, $this->getResource($file)); @@ -72,6 +74,11 @@ public function onEnable(){ $shops = yaml_parse_file($this->getDataFolder().'shops.yml'); if(!empty($shops)) foreach($shops as $key => $val) $this->shops[$key] = $val; + + $config = yaml_parse_file($this->getDataFolder().'config.yml'); + $this->defaultprice = $config["default-price"] ?? 15000; + $this->notallowed = array_flip($config["banned-items"] ?? []); + $this->helpcmd = [ TF::YELLOW.TF::BOLD.'Chest Shop'.TF::RESET, TF::YELLOW.'/{:cmd:} add [price]'.TF::GRAY.' - Add the item in your hand to the chest shop.', @@ -100,7 +107,7 @@ public function sendChestShop(Player $player){ ]); /** @var Chest $tile */ $tile = Tile::createTile('CustomChest', $player->chunk, $nbt); - $tile->namedtag->replace = new IntTag("replace", $tile->getBlock()->getId()); + $tile->namedtag->replace = new IntArrayTag("replace", [$tile->getBlock()->getId(), $tile->getBlock()->getDamage()]); $block = Block::get(Block::CHEST); $block->x = floor($tile->x); $block->y = floor($tile->y); @@ -117,9 +124,12 @@ public function reload(){ $this->shops = []; $shops = yaml_parse_file($this->getDataFolder().'shops.yml'); if(!empty($shops)) foreach($shops as $key => $val) $this->shops[$key] = $val; + $config = yaml_parse_file($this->getDataFolder().'config.yml'); + $this->defaultprice = $config["default-price"] ?? 15000; + $this->notallowed = array_flip($config["banned-items"] ?? []); } - public function getItemFromShop(int $id) : Item{ + public function getItemFromShop(int $id): Item { $data = $this->shops[$id] ?? null; $item = null; if(is_array($data)){ @@ -139,7 +149,7 @@ public function fillInventoryWithShop($inventory, $page = 0){ $item = Item::get($data[0], $data[1], $data[2]); if($data[3] === null) break; $item->setNamedTag(unserialize($data[3])); - $item->setCustomName(TF::RESET.TF::YELLOW.'Tap again to purchase for $'.$item->getNamedTag()->ChestShop->getValue()[0].TF::RESET."\n".' '."\n".$item->getName()); + $item->setCustomName(TF::RESET.$item->getName()."\n \n".TF::YELLOW.'Double-tap to purchase for $'.$item->getNamedTag()->ChestShop->getValue()[0].TF::RESET); $inventory->addItem($item); } } @@ -148,11 +158,12 @@ public function fillInventoryWithShop($inventory, $page = 0){ $turnleft = Item::get(Item::PAPER); $turnright = Item::get(Item::PAPER); $turnleft->setCustomName(TF::RESET.TF::GOLD.TF::BOLD.'<< Turn Left'.TF::RESET."\n".TF::GRAY.'Turn towards the left.'); - $turnright->setCustomName(TF::RESET.TF::GOLD.TF::BOLD.' Turn Right'.TF::RESET."\n".TF::GRAY.'Turn towards the right.'); + $turnright->setCustomName(TF::RESET.TF::GOLD.TF::BOLD.'Turn Right >>'.TF::RESET."\n".TF::GRAY.'Turn towards the right.'); $nbtleft = $turnleft->getNamedTag(); $nbtleft->turner = new IntArrayTag('turner', [0, $page]); $turnleft->setNamedTag($nbtleft); + $nbtright = $turnright->getNamedTag(); $nbtright->turner = new IntArrayTag('turner', [1, $page]); $turnright->setNamedTag($nbtright); @@ -163,21 +174,29 @@ public function fillInventoryWithShop($inventory, $page = 0){ public function addToChestShop(Item $item, int $price){ $key = rand(); - $nbt = $item->getNamedTag() !== null ? serialize($item->getNamedTag()) : null; $nbt = $item->getNamedTag() ?? new CompoundTag("", []); - $nbt->ChestShop = new IntArrayTag ('ChestShop', [$price, $key]); + $nbt->ChestShop = new IntArrayTag('ChestShop', [$price, $key]); $nbt->CSKey = $key; $item->setNamedTag($nbt); - $this->shops[$key] = [$item->getId(), $item->getDamage(), $item->getCount(), $nbt]; + $this->shops[$key] = [$item->getId(), $item->getDamage(), $item->getCount(), serialize($nbt)]; } public function removeItemOffShop(int $page, int $slot){ if(empty($this->shops)) return; - $keys = array_keys($this->shops);//$shops is an associative array. - $key = (24*$page) + $slot;//array_chunks divides $shops into 24 parts in the GUI. Hope PHP follows BODMAS. + $keys = array_keys($this->shops);//$this->shops is an associative array. + $key = (24*$page) + $slot;//array_chunks divides $shops into 24 parts in the GUI. unset($this->shops[$keys[--$key]]);//$slot - 1. Slots are counted from 0. If $slot is 1, the issuer probably (actually) is referring to slot zero. } + private function isNotAllowed(int $itemId, int $itemDamage = 0) : bool{ + if($itemDamage === 0) return isset($this->notallowed[$itemId]) || isset($this->notallowed[$itemId.':'.$itemDamage]); + else return isset($this->notallowed[$itemId.':'.$itemDamage]); + } + + public function removeItemsByKey(array $keys){ + foreach($keys as $key) unset($this->shops[$key]); + } + public function onCommand(CommandSender $sender, Command $cmd, $label, array $args){ if(isset($args[0])){ switch(strtolower($args[0])){ @@ -191,19 +210,39 @@ public function onCommand(CommandSender $sender, Command $cmd, $label, array $ar if($sender->hasPermission('chestshop.command.add')){ $item = $sender->getInventory()->getItemInHand(); if($item->getId() === 0) $sender->sendMessage(self::PREFIX.TF::RED.'Please hold an item in your hand.'); + elseif($this->isNotAllowed($item->getId(), $item->getDamage())) $sender->sendMessage(self::PREFIX.TF::RED.'You cannot sell '.((Item::get($item->getId(), $item->getDamage()))->getName()).' on /chestshop.'); else{ - if(isset($args[1]) && is_numeric($args[1]) && $args[1] >= 0) $this->addToChestShop($item, $args[1]); - else $sender->sendMessage(TF::RED.'Please enter a valid number.'); + if(isset($args[1]) && is_numeric($args[1]) && $args[1] >= 0) { + $sender->sendMessage(self::PREFIX.TF::YELLOW.'Added '.(explode("\n", $item->getName())[0]).' to '.$cmd->getName().' for $'.$args[1].'.'); + $this->addToChestShop($item, $args[1]); + }else $sender->sendMessage(TF::RED.'Please enter a valid number.'); } } break; + case "removebyid": + if($sender->hasPermission('chestshop.command.remove')){ + if(isset($args[1]) && is_numeric($args[1]) && $args[1] >= 1){ + $damage = $args[2] ?? 0; + if(count($this->shops) <= 27){ + $i = 0; + foreach($this->shops as $k => $item){ + if($item[0] == $args[1] && $item[1] == $damage){ + unset($this->shops[$k]); + ++$i; + } + } + $sender->sendMessage(self::PREFIX.TF::YELLOW.$i.' items were removed off auction house (ID: '.$args[1].', DAMAGE: '.$damage.').'); + }else $this->getServer()->getScheduler()->scheduleAsyncTask(new RemoveByIdTask([$sender->getName(), $args[1], $damage, &$this->shops])); + }else $sender->sendMessage(self::PREFIX.TF::YELLOW.'Usage: /'.$cmd->getName().' removebyid [item-id] [item-damage]'); + } + break; case "remove": if($sender->hasPermission('chestshop.command.remove')){ if(isset($args[1], $args[2]) && is_numeric($args[1]) && is_numeric($args[2]) && ($args[1] >= 0) && ($args[2] >= 1)){ $sender->sendMessage(self::PREFIX.TF::YELLOW.'Removed item on page #'.$args[1].', slot #'.$args[2].'.'); $this->removeItemOffShop($args[1], $args[2]); } else $sender->sendMessage(TF::RED.'Page number and item slot must be integers (page > -1, slot > 0).'); - } + } break; case "reload": if($sender->hasPermission('chestshop.command.reload')){ diff --git a/src/ChestShop/RemoveByIdTask.php b/src/ChestShop/RemoveByIdTask.php new file mode 100644 index 0000000..26f9253 --- /dev/null +++ b/src/ChestShop/RemoveByIdTask.php @@ -0,0 +1,40 @@ +data = $data; + } + + public function onRun(){ + $res = []; + foreach($this->data[3] as $k => $v){ + if($v[0] == $this->data[1] && $v[1] == $this->data[2]){ + $res[] = $k; + } + } + $this->setResult($res); + } + + public function onCompletion(Server $server){ + $res = $this->getResult(); + if(($player = $server->getPlayerExact($this->data[0])) instanceof Player){ + $player->sendMessage(Main::PREFIX.TF::YELLOW.count($res).' items were removed off auction house (ID: '.$this->data[1].', DAMAGE: '.$this->data[2].').'); + } + $server->getPluginManager()->getPlugin("ChestShop")->removeItemsByKey($res); + } +} \ No newline at end of file