From 63fa9fa76ddab9cd95ed20cbb4b737cbb6c4a790 Mon Sep 17 00:00:00 2001 From: Muqsit Rayyan Date: Tue, 11 Apr 2017 07:40:39 +0300 Subject: [PATCH] ChestShop v3.0 https://github.com/Muqsit/ChestShop/releases/tag/v3.0 --- plugin.yml | 6 +- src/ChestShop/Chest/CustomChest.php | 32 +++- src/ChestShop/Chest/CustomChestInventory.php | 8 +- src/ChestShop/EventListener.php | 18 +- src/ChestShop/Main.php | 178 ++++++++++++++++--- 5 files changed, 202 insertions(+), 40 deletions(-) diff --git a/plugin.yml b/plugin.yml index 9e8a909..dba7eec 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,12 +1,14 @@ name: ChestShop main: ChestShop\Main -api: [2.0.0, 3.0.0, 3.0.0-ALPHA4] -version: 2.0 +api: [2.0.0, 3.0.0, 3.0.0-ALPHA4, 3.0.0-ALPHA5, 3.0.0-ALPHA6] +version: 3.0 depend: [EconomyAPI] commands: chestshop: aliases: ["cs", "cshop"] permissions: + chestshop.command.opcmd: + default: op chestshop.command.add: default: op chestshop.command.remove: diff --git a/src/ChestShop/Chest/CustomChest.php b/src/ChestShop/Chest/CustomChest.php index 7bd382d..631e716 100644 --- a/src/ChestShop/Chest/CustomChest.php +++ b/src/ChestShop/Chest/CustomChest.php @@ -26,16 +26,42 @@ use pocketmine\block\Block; use pocketmine\level\Level; use pocketmine\nbt\tag\{CompoundTag, IntTag}; +use pocketmine\Player; class CustomChest extends \pocketmine\tile\Chest{ + private $replacement = [0, 0]; + public function __construct(Level $level, CompoundTag $nbt){ parent::__construct($level, $nbt); $this->inventory = new CustomChestInventory($this); + $this->replacement = [$this->getBlock()->getId(), $this->getBlock()->getDamage()]; + } + + public function getInventory() : CustomChestInventory{ + return $this->inventory; + } + + private function getReplacement() : Block{ + return Block::get(...$this->replacement); + } + + public function sendReplacement(Player $player){ + $block = $this->getReplacement(); + $block->x = floor($this->x); + $block->y = floor($this->y); + $block->z = floor($this->z); + $block->level = $this->getLevel(); + if($block->level !== null){ + $block->level->sendBlocks([$player], [$block]); + } + } + + public function spawnTo(Player $player){ + //needless } - public function getReplacement() : Block{ - $replace = $this->namedtag->replace->getValue() ?? [0, 0]; - return Block::get($replace[0], $replace[1]); + public function spawnToAll(){ + //needless } } diff --git a/src/ChestShop/Chest/CustomChestInventory.php b/src/ChestShop/Chest/CustomChestInventory.php index c3b54fa..ffbb4d7 100644 --- a/src/ChestShop/Chest/CustomChestInventory.php +++ b/src/ChestShop/Chest/CustomChestInventory.php @@ -37,13 +37,7 @@ public function onOpen(Player $who){ } public function onClose(Player $who){ - $pos = $this->holder;//not really "pos".. but our usage definitely makes it a pos. - $block = $pos->getReplacement(); - $block->x = floor($pos->x); - $block->y = floor($pos->y); - $block->z = floor($pos->z); - $block->level = $pos->getLevel(); - if($who instanceof Player) $block->level->sendBlocks([$who], [$block]); + $this->holder->sendReplacement($who); 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 040c13a..90baad3 100644 --- a/src/ChestShop/EventListener.php +++ b/src/ChestShop/EventListener.php @@ -25,6 +25,7 @@ use ChestShop\Chest\{CustomChest, CustomChestInventory}; use onebone\economyapi\EconomyAPI; + use pocketmine\event\block\BlockBreakEvent; use pocketmine\event\inventory\InventoryTransactionEvent; use pocketmine\event\Listener; @@ -72,23 +73,32 @@ public function onTransaction(InventoryTransactionEvent $event){ } } + if(($player ?? $chestinv ?? $action) === null){ + return; + } + /* * $player => Player interacting with the GUI. * $chestinv => The chest's inventory. * $action => BaseTransaction|Transaction|SimpleTransactionGroup */ $event->setCancelled(); - $item = ($item = $action->getTargetItem())->getId() === 0 ? $action->getSourceItem() : $item; + $item = $action->getSourceItem(); + if($item->getId() === Item::AIR){ + return; + } if(isset($item->getNamedTag()->turner)){ - $action = $item->getNamedTag()->turner->getValue(); - $page = $action[0] === 0 ? --$action[1] : ++$action[1]; + $pagedata = $item->getNamedTag()->turner->getValue(); + $page = $pagedata[0] === 0 ? --$pagedata[1] : ++$pagedata[1]; $this->plugin->fillInventoryWithShop($chestinv, $page); return; } $data = isset($item->getNamedTag()->ChestShop) ? $item->getNamedTag()->ChestShop->getValue() : null; - if($data === null) return; + if($data === null){ + return; + } $price = $data[0] ?? $this->plugin->defaultprice; if(!isset($this->plugin->clicks[$player->getId()][$data[1]])){ diff --git a/src/ChestShop/Main.php b/src/ChestShop/Main.php index 8c00307..0398bff 100644 --- a/src/ChestShop/Main.php +++ b/src/ChestShop/Main.php @@ -23,26 +23,39 @@ */ namespace ChestShop; -use ChestShop\Chest\CustomChest; -use pocketmine\plugin\PluginBase; -use pocketmine\Player; -use pocketmine\tile\Tile; -use pocketmine\nbt\tag\{CompoundTag, IntTag, ListTag, StringTag, IntArrayTag}; +use ChestShop\Chest\{CustomChest, CustomChestInventory}; + use pocketmine\command\{Command, CommandSender}; -use pocketmine\utils\TextFormat as TF; use pocketmine\block\Block; use pocketmine\item\Item; +use pocketmine\nbt\tag\{CompoundTag, IntTag, ListTag, StringTag, IntArrayTag}; +use pocketmine\Player; +use pocketmine\plugin\PluginBase; +use pocketmine\tile\Tile; +use pocketmine\utils\TextFormat as TF; class Main extends PluginBase{ const PREFIX = TF::BOLD.TF::YELLOW.'CS '.TF::RESET; + const CONFIG = [ + 'config.yml' => [ + 'default-price' => 15000, + 'banned-items' => [ + '35:1', + 7, + ], + 'enable-sync' => false + ] + ]; + public $defaultprice; public $inChestShop, $clicks = []; protected $shops = []; private $helpcmd = []; private static $instance = null; private $notallowed = []; + private $economyshop = false; public function onEnable(){ self::$instance = $this; @@ -64,12 +77,8 @@ public function onEnable(){ $this->getLogger()->notice(implode("\n", $info)); if(!is_dir($this->getDataFolder())) mkdir($this->getDataFolder()); - 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)); - fclose($openf); - } + foreach(array_keys(self::CONFIG) as $file){ + $this->updateConfig($file); } $shops = yaml_parse_file($this->getDataFolder().'shops.yml'); @@ -86,8 +95,17 @@ public function onEnable(){ TF::YELLOW.'/{:cmd:} reload'.TF::GRAY.' - Reload the plugin (to fix errors or refresh data).' ]; Tile::registerTile(CustomChest::class); + + if($config['enable-sync'] == true){ + $this->economyshop = $this->getServer()->getPluginManager()->getPlugin('EconomyShop'); + }else{ + $this->economyshop = false; + } } + /** + * @return Main + */ public static function getInstance(){ return self::$instance; } @@ -96,9 +114,32 @@ public function onDisable(){ yaml_emit_file($this->getDataFolder().'shops.yml', $this->shops); } + /** + * Updates config with newer data. + */ + private function updateConfig(string $config){ + if(isset(self::CONFIG[$config])){ + $data = []; + if(is_file($path = $this->getDataFolder().$config)){ + $data = yaml_parse_file($path); + } + foreach(self::CONFIG[$config] as $key => $value){ + if(!isset($data[$key])){ + $data[$key] = $value; + } + } + yaml_emit_file($path, $data); + } + } + + /** + * Sends the chest shop (inventory) + * to the player. + * + * @param Player $player + */ public function sendChestShop(Player $player){ $nbt = new CompoundTag('', [ - new ListTag('Items', []), new StringTag('id', Tile::CHEST), new IntTag('ChestShop', 1), new IntTag('x', floor($player->x)), @@ -107,15 +148,13 @@ public function sendChestShop(Player $player){ ]); /** @var Chest $tile */ $tile = Tile::createTile('CustomChest', $player->getLevel(), $nbt); - $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); $block->z = floor($tile->z); $block->level = $tile->getLevel(); $block->level->sendBlocks([$player], [$block]); - $inventory = $tile->getInventory(); - $this->fillInventoryWithShop($inventory); + $this->fillInventoryWithShop($inventory = $tile->getInventory()); $player->addWindow($inventory); } @@ -129,10 +168,16 @@ public function reload(){ $this->notallowed = array_flip($config["banned-items"] ?? []); } + /** + * Get item from shop via shop ID. + * + * @param int $id + * @return Item + */ public function getItemFromShop(int $id): Item { $data = $this->shops[$id] ?? null; $item = null; - if(is_array($data)){ + if($data !== null){ $item = Item::get($data[0], $data[1], $data[2]); $item->setNamedTag(unserialize($data[3])); unset($item->getNamedTag()->ChestShop); @@ -140,10 +185,20 @@ public function getItemFromShop(int $id): Item { return $item ?? Item::get(0); } - public function fillInventoryWithShop($inventory, $page = 0){ + /** + * Fills the $inventory with contents + * of chest shop. + * + * @param ChestInventory $inventory + * @param int $page + */ + public function fillInventoryWithShop(CustomChestInventory $inventory, int $page = 0){ $inventory->clearAll(); if(!empty($this->shops)) { $chunked = array_chunk($this->shops, 24, true); + if($page < 0){ + $page = count($chunked) - 1; + } $page = isset($chunked[$page]) ? $page : 0; foreach($chunked[$page] as $data){ $item = Item::get($data[0], $data[1], $data[2]); @@ -172,8 +227,19 @@ public function fillInventoryWithShop($inventory, $page = 0){ $inventory->setItem(26, $turnright); } + /** + * Adds an item to the chest shop. + * + * @param Item $item + * @param int $price + */ public function addToChestShop(Item $item, int $price){ $key = rand(); + if(isset($this->shops[$key])){ + while(isset($this->shops[$key])){ + $key = rand(); + } + } $nbt = $item->getNamedTag() ?? new CompoundTag("", []); $nbt->ChestShop = new IntArrayTag('ChestShop', [$price, $key]); $nbt->CSKey = $key; @@ -181,6 +247,12 @@ public function addToChestShop(Item $item, int $price){ $this->shops[$key] = [$item->getId(), $item->getDamage(), $item->getCount(), serialize($nbt)]; } + /** + * Removes an item off the chest shop. + * + * @param int $page + * @param int $slot + */ public function removeItemOffShop(int $page, int $slot){ if(empty($this->shops)) return; $keys = array_keys($this->shops);//$this->shops is an associative array. @@ -188,13 +260,33 @@ public function removeItemOffShop(int $page, int $slot){ 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. } + /** + * Checks whether or not it's allowed + * to put an item in the chest shop. + * This can be set up in the config + * (banned-items key in the config). + * + * @param int $itemId + * @param int $itemDamage + * @return bool + */ 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]); + if($itemDamage === 0){ + return isset($this->notallowed[$itemId]) || isset($this->notallowed[$itemId.':'.$itemDamage]); + } + return isset($this->notallowed[$itemId.':'.$itemDamage]); } - public function removeItemsByKey(array $keys){ - foreach($keys as $key) unset($this->shops[$key]); + /** + * Removes item off chest shop + * by key. + * + * @param int|array $keys + */ + public function removeItemsByKey($keys){ + foreach((array)$keys as $key){ + unset($this->shops[$key]); + } } public function onCommand(CommandSender $sender, Command $cmd, $label, array $args){ @@ -204,7 +296,7 @@ public function onCommand(CommandSender $sender, Command $cmd, $label, array $ar $sender->sendMessage(str_replace('{:cmd:}', $cmd, implode("\n", $this->helpcmd))); break; case "about": - $sender->sendMessage(TF::YELLOW.TF::BOLD.'ChestShop'.TF::RESET."\n".TF::GRAY.'Coded by Muqsit Rayyan ('.TF::AQUA.'@muqsitrayyan'.TF::GRAY.').'); + $sender->sendMessage(TF::YELLOW.TF::BOLD.'ChestShop'.TF::RESET."\n".TF::GRAY.'Created by Muqsit ('.TF::AQUA.'@muqsitrayyan'.TF::GRAY.').'); break; case "add": if($sender->hasPermission('chestshop.command.add')){ @@ -251,10 +343,48 @@ public function onCommand(CommandSender $sender, Command $cmd, $label, array $ar $sender->sendMessage(self::PREFIX.TF::AQUA.'ChestShop has reloaded successfully.'); } break; + case "synceconomy": + if($sender->hasPermission('chestshop.command.opcmd')){ + switch($this->economyshop){ + case null: + $sender->sendMessage(TF::RED."Couldn't find EconomyShop plugin. Make sure the plugin is enabled and running."); + return false; + case false: + $sender->sendMessage(TF::RED.'You must set the "enable-sync" option to true in the config to use this command.'); + return false; + } + $depends = $this->economyshop->getDescription()->getVersion() == '2.0.3'; + $data = yaml_parse_file($this->economyshop->getDataFolder().'Shops.yml'); + $cnt = count($data); + $keyId = $depends ? 'item' : 4; + $keyDmg = $depends ? 'meta' : 5; + $keyCnt = $depends ? 'amount' : 6; + $keyPrice = $depends ? 'price' : 8; + $i = 0; + $this->getLogger()->info($sender->getName().' is synchronizing data from economyshop.'); + $time = microtime(true); + foreach($data as $detail){ + $item = Item::get($detail[$keyId], $detail[$keyDmg], $detail[$keyCnt]); + $price = $detail[$keyPrice]; + $this->addToChestShop($item, $price); + $sender->sendMessage(TF::YELLOW.'Synchronizing data from EconomyShop '.TF::GREEN.'('.TF::GRAY.++$i.TF::GREEN.'/'.TF::GRAY.$cnt.TF::GREEN.')'); + } + $time = microtime(true) - $time; + $sender->sendMessage(TF::YELLOW.'Data synchronized. Took '.TF::GREEN.$time.TF::YELLOW.'s.'); + $this->getLogger()->info($sender->getName().' has synchronized data from chestshop.'); + } + break; default: $sender->sendMessage(TF::RED.'Type /cs help to get a list of chest shop help commands.'); break; } - }else $this->sendChestShop($sender); + }else{ + if($sender instanceof Player){ + $this->sendChestShop($sender); + }else{ + $sender->sendMessage(str_replace('{:cmd:}', $cmd, implode("\n", $this->helpcmd))); + } + } + return true; } }