├── .gitignore ├── resources └── config.yml ├── plugin.yml ├── .travis.yml ├── phpunit.xml ├── src └── Buycraft │ └── PocketMine │ ├── Util │ ├── RunAsyncTask.php │ ├── FinalizeReportTask.php │ ├── AnalyticsSend.php │ ├── InventoryUtils.php │ ├── PackageInventory.php │ ├── CategoryInventory.php │ └── ReportUtil.php │ ├── Execution │ ├── DeleteCommandsAsyncTask.php │ ├── CategoryRefreshTask.php │ ├── ImmediateExecutionRunner.php │ ├── PlayerCommandExecutor.php │ ├── CommandExecutor.php │ ├── DeleteCommandsTask.php │ ├── QueuedCommand.php │ └── DuePlayerCheck.php │ ├── Commands │ ├── BuyCommand.php │ ├── SecretVerificationTask.php │ ├── BuycraftCommandAlias.php │ └── BuycraftCommand.php │ ├── PluginApi.php │ ├── BuycraftListener.php │ └── BuycraftPlugin.php ├── README.md ├── LICENSE.md └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.phar -------------------------------------------------------------------------------- /resources/config.yml: -------------------------------------------------------------------------------- 1 | # BuycraftPM Configuration 2 | secret: '' -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | name: Tebex-PMMP 2 | main: Buycraft\PocketMine\BuycraftPlugin 3 | version: 2.0.1 4 | author: Tebex Limited 5 | api: 3.0.0 6 | api-version: 3.0.0 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.1 4 | 5 | matrix: 6 | fast_finish: true 7 | 8 | script: 9 | - phpunit 10 | - pwd 11 | - ./build-plugin.sh 12 | 13 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | 15 | 16 | 17 | 18 | app/ 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/RunAsyncTask.php: -------------------------------------------------------------------------------- 1 | asyncTask = $asyncTask; 22 | } 23 | 24 | /** 25 | * Actions to execute when run 26 | * 27 | * @param $currentTick 28 | * 29 | * @return void 30 | */ 31 | public function onRun(int$currentTick) 32 | { 33 | Server::getInstance()->getAsyncPool()->submitTask($this->asyncTask); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/DeleteCommandsAsyncTask.php: -------------------------------------------------------------------------------- 1 | pluginApi = $pluginApi; 22 | $this->commands = $commands; 23 | } 24 | 25 | /** 26 | * Actions to execute when run 27 | * 28 | * @param $currentTick 29 | * 30 | * @return void 31 | */ 32 | public function onRun() 33 | { 34 | $this->pluginApi->deleteCommands((array)$this->commands); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Commands/BuyCommand.php: -------------------------------------------------------------------------------- 1 | plugin = $plugin; 19 | } 20 | 21 | public function execute(CommandSender $sender, string $commandLabel, array $args): bool 22 | { 23 | if ($sender instanceof Player) { 24 | $this->plugin->getInventoryUtils()->showCategoryGui($sender); 25 | } else { 26 | $this->plugin->getLogger()->error("Only in-game players can execute the /buy command"); 27 | } 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/FinalizeReportTask.php: -------------------------------------------------------------------------------- 1 | lines = $lines; 19 | $this->fn = BuycraftPlugin::getInstance()->getDataFolder() . 'report-' . date('Y-m-d-H-i-s') . '.txt'; 20 | } 21 | 22 | /** 23 | * Actions to execute when run 24 | * 25 | * @return void 26 | */ 27 | public function onRun() 28 | { 29 | $ss = ReportUtil::generateServiceStatus(); 30 | $result = implode("\n", array_merge((array)$this->lines, $ss)); 31 | file_put_contents($this->fn, $result); 32 | } 33 | 34 | public function onCompletion(Server $server) 35 | { 36 | BuycraftPlugin::getInstance()->getLogger()->info("Report saved to " . $this->fn); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/CategoryRefreshTask.php: -------------------------------------------------------------------------------- 1 | plugin = $plugin; 18 | } 19 | 20 | public function onRun(int $currentTick) 21 | { 22 | $this->plugin->getLogger()->info("Refreshing category list..."); 23 | 24 | $pluginApi = $this->plugin->getPluginApi(); 25 | try { 26 | $request = $pluginApi->basicGet("/listing", true, 10); 27 | $this->plugin->setCategories($request['categories']); 28 | 29 | $this->plugin->getLogger()->info("Category refresh complete."); 30 | } catch (\Exception $e) { 31 | //$this->plugin->getLogger()->logException($e); 32 | $this->plugin->getLogger()->error(TextFormat::RED . "Unable to fetch category listing."); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BuycraftPM 2 | 3 | BuycraftPM is the official port of the Buycraft plugin to PocketMine-MP. BuycraftPM closely follows BuycraftX in both 4 | functionality and behavior as much as possible. 5 | 6 | ## Contributing 7 | 8 | We welcome contributions from the community. Please refer to the CONTRIBUTING.md file for more details. By submitting code to us, you agree to the 9 | terms set out in the CONTRIBUTING.md file 10 | 11 | 12 | ## Compatibility 13 | 14 | We are committed to making sure that BuycraftPM works on as many PocketMine-MP forks as reasonably possible. The Buycraft 15 | team tests and ensures that full functionality for BuycraftPM is available on **PocketMine-MP**, **ClearSky** and **Genisys**. 16 | 17 | Other forks may work, but are not tested by Buycraft and you may be unable to obtain support for issues that arise. 18 | 19 | ## Building the plugin 20 | 21 | To build the plugin, run `./build-plugin.sh` on any *nix host (OS X and Linux are tested by us). 22 | 23 | ## Support 24 | If you are a Buycraft customer and you need any assistance with this plugin, please contact our support team through your Buycraft account. 25 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/AnalyticsSend.php: -------------------------------------------------------------------------------- 1 | json = $json; 28 | $this->secret = $secret; 29 | } 30 | 31 | public static function sendAnalytics(BuycraftPlugin $plugin) 32 | { 33 | 34 | //noop 35 | } 36 | 37 | /** 38 | * Actions to execute when run 39 | * 40 | * @return void 41 | */ 42 | public function onRun() 43 | { 44 | //noop 45 | } 46 | 47 | public function onCompletion(Server $server) 48 | { 49 | //noop 50 | } 51 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Buycraft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/ImmediateExecutionRunner.php: -------------------------------------------------------------------------------- 1 | pluginApi = $pluginApi; 22 | } 23 | 24 | /** 25 | * Actions to execute when run 26 | * 27 | * @return void 28 | */ 29 | public function onRun() 30 | { 31 | try { 32 | $response = $this->pluginApi->basicGet("/queue/offline-commands"); 33 | $this->setResult($response->commands); 34 | } catch (\Exception $e) { 35 | $this->setResult($e); 36 | return; 37 | } 38 | } 39 | 40 | public function onCompletion(Server $server) 41 | { 42 | if ($this->getResult() instanceof \Exception) { 43 | //BuycraftPlugin::getInstance()->getLogger()->logException($this->getResult()); 44 | BuycraftPlugin::getInstance()->getLogger()->error(TextFormat::RED . "Unable to fetch offline commands."); 45 | return; 46 | } 47 | foreach ($this->getResult() as $command) { 48 | BuycraftPlugin::getInstance() 49 | ->getCommandExecutionTask() 50 | ->queue($command, $command->player->name, false, $command->player->uuid ? $command->player->uuid : ""); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/PlayerCommandExecutor.php: -------------------------------------------------------------------------------- 1 | pluginApi = $pluginApi; 25 | $this->due = $due; 26 | } 27 | 28 | /** 29 | * Actions to execute when run 30 | * 31 | * @return void 32 | */ 33 | public function onRun() 34 | { 35 | try { 36 | $this->setResult($this->pluginApi->basicGet('/queue/online-commands/' . $this->due->id)->commands); 37 | } catch (\Exception $e) { 38 | $this->setResult($e); 39 | return; 40 | } 41 | } 42 | 43 | public function onCompletion(Server $server) 44 | { 45 | if ($this->getResult() instanceof \Exception) { 46 | //BuycraftPlugin::getInstance()->getLogger()->logException($this->getResult()); 47 | BuycraftPlugin::getInstance()->getLogger()->error(TextFormat::RED . "Unable to fetch online commands for player."); 48 | return; 49 | } 50 | foreach ($this->getResult() as $command) { 51 | BuycraftPlugin::getInstance() 52 | ->getCommandExecutionTask() 53 | ->queue($command, $this->due->name, true, $this->due->uuid ? $this->due->uuid : ""); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Commands/SecretVerificationTask.php: -------------------------------------------------------------------------------- 1 | secret = $secret; 25 | $this->dataFolder = $dataFolder; 26 | } 27 | 28 | /** 29 | * Actions to execute when run 30 | * 31 | * @return void 32 | */ 33 | public function onRun() 34 | { 35 | try { 36 | $api = new PluginApi($this->secret, $this->dataFolder); 37 | $this->setResult($api->basicGet("/information")); 38 | } catch (\Exception $e) { 39 | $this->setResult($e); 40 | } 41 | } 42 | 43 | public function onCompletion(Server $server) 44 | { 45 | $result = $this->getResult(); 46 | if ($result instanceof \Exception) { 47 | //BuycraftPlugin::getInstance()->getLogger()->logException($result); 48 | BuycraftPlugin::getInstance()->getLogger()->error(TextFormat::RED . "This secret key appears to be invalid. Try again."); 49 | } else { 50 | 51 | BuycraftPlugin::getInstance()->changeApi(new PluginApi($this->secret, $this->dataFolder), $result); 52 | BuycraftPlugin::getInstance()->getConfig()->set('secret', $this->secret); 53 | BuycraftPlugin::getInstance()->getLogger()->info(TextFormat::GREEN . "Secret set!"); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/CommandExecutor.php: -------------------------------------------------------------------------------- 1 | commands as $id => $command) { 32 | if (count($successfully_executed) >= self::MAXIMUM_COMMANDS_TO_RUN) { 33 | break; 34 | } 35 | 36 | if ($command->canExecute()) { 37 | // TODO: Capture command exceptions for our use. 38 | if (Server::getInstance()->dispatchCommand(new ConsoleCommandSender(), $command->getFinalCommand())) { 39 | $successfully_executed[] = $command; 40 | } 41 | } 42 | } 43 | 44 | // Now queue all the successfully run commands to be removed from the command queue. 45 | foreach ($successfully_executed as $executed) { 46 | BuycraftPlugin::getInstance()->getDeleteCommandsTask()->queue($executed->getCommandId()); 47 | unset($this->commands[$executed->getCommandId()]); 48 | } 49 | } 50 | 51 | public function queue($command, $username, $online, $xuid = '') 52 | { 53 | $this->commands[$command->id] = new QueuedCommand($command, $username, $online, $xuid); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Commands/BuycraftCommandAlias.php: -------------------------------------------------------------------------------- 1 | plugin = $plugin; 28 | } 29 | 30 | /** 31 | * @param CommandSender $sender 32 | * @param string $commandLabel 33 | * @param string[] $args 34 | * 35 | * @return mixed 36 | */ 37 | public function execute(CommandSender $sender, string $commandLabel, array $args) :bool 38 | { 39 | Server::getInstance()->dispatchCommand($sender, "tebex " . implode(" ", $args)); 40 | return true; 41 | } 42 | 43 | private function sendHelp(CommandSender $sender) 44 | { 45 | $sender->sendMessage(TextFormat::GREEN . "Usage for the Tebex-PMMP plugin:"); 46 | $sender->sendMessage(TextFormat::GREEN . "/tebex:secret" . TextFormat::GRAY . ": Set your server's secret."); 47 | $sender->sendMessage(TextFormat::GREEN . "/tebex:forcecheck" . TextFormat::GRAY . ": Check for current purchases."); 48 | $sender->sendMessage(TextFormat::GREEN . "/tebex:info" . TextFormat::GRAY . ": Retrieves public information about your web store."); 49 | $sender->sendMessage(TextFormat::GREEN . "/tebex:report" . TextFormat::GRAY . ": Generates a report you can send to Buycraft support."); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to BuycraftPM 2 | 3 | Contributions to the BuycraftPM codebase must be made by using your own fork of our repository and then submitting pull requests. 4 | 5 | Contributions can take the form of new components/features, changes to existing features, tests, documentation, bug fixes or optimizations. 6 | 7 | The Buycraft team will review all contributions, and their decision is final. During the review process they may ask questions of your contributions, or request alterations or fixes. The Buycraft team reserve the right to close any pull request or issue for any reason. 8 | 9 | ## Terms of Contributing 10 | 11 | By making a contribution, you agree to the terms below, which includes granting Tebex Ltd a royalty-free, perpetual and irrevocable license to use and relicense your contribution under any terms deemed fit to Tebex. You may only contribute software and materials originally created by you or your organisation and you must have the right to make such contributions. 12 | 13 | Subject to the terms and conditions of this Agreement, you grant to Tebex and all third party recipients of software, products, services, and information distributed by Tebex a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute the contributed code and such derivative works. 14 | 15 | By submitting code for including in the BuycraftPM codebase, you represent that each code submission is of your own original creation and they you hold the copyright for such works, or are authorized to submit by the copyright holder. 16 | 17 | You agree to waive all other claims of any nature, including express contract, implied-in-fact contract, or quasi-contract, arising out of any submission of code to Tebex. 18 | 19 | The terms of contributing may be changed by us at any time, and such changes will be published in this file. Before each submission, you should review this file, and if you no longer agree to the terms, stop submitting code to us. -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/DeleteCommandsTask.php: -------------------------------------------------------------------------------- 1 | pluginApi = $pluginApi; 24 | } 25 | 26 | /** 27 | * Actions to execute when run 28 | * 29 | * @param $currentTick 30 | * 31 | * @return void 32 | */ 33 | public function onRun(int$currentTick) 34 | { 35 | $available = count($this->commandIds); 36 | if ($available > self::MAXIMUM_COMMANDS_TO_POST) { 37 | // Only consider the first MAXIMUM_COMMANDS_TO_POST commands. 38 | $toPost = array_slice($this->commandIds, 0, self::MAXIMUM_COMMANDS_TO_POST); 39 | $this->commandIds = array_slice($this->commandIds, self::MAXIMUM_COMMANDS_TO_POST); 40 | } else { 41 | // Copy the array 42 | $toPost = $this->commandIds; 43 | $this->commandIds = array(); 44 | } 45 | 46 | if (isset($toPost) && count($toPost) > 0) { 47 | BuycraftPlugin::getInstance()->getServer()->getAsyncPool()->submitTask(new DeleteCommandsAsyncTask($this->pluginApi, $toPost)); 48 | } 49 | } 50 | 51 | /** 52 | * Immediately purges all queued commands. 53 | */ 54 | public function sendAllCommands() 55 | { 56 | if (count($this->commandIds) > self::MAXIMUM_COMMANDS_TO_POST) { 57 | $chunked = array_chunk($this->commandIds, self::MAXIMUM_COMMANDS_TO_POST); 58 | foreach ($chunked as $chunk) { 59 | BuycraftPlugin::getInstance()->getPluginApi()->deleteCommands($chunk); 60 | } 61 | } else { 62 | BuycraftPlugin::getInstance()->getPluginApi()->deleteCommands($this->commandIds); 63 | } 64 | } 65 | 66 | /** 67 | * Queues a command to be marked complete. 68 | * @param $id integer 69 | */ 70 | public function queue($id) 71 | { 72 | if (!in_array($id, $this->commandIds)) { 73 | $this->commandIds[] = $id; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/QueuedCommand.php: -------------------------------------------------------------------------------- 1 | command = $command; 25 | $this->username = $username; 26 | $this->xuid = $xuid; 27 | $this->queuedTime = time(); 28 | $this->needOnline = $needOnline; 29 | } 30 | 31 | public function getCommandId() 32 | { 33 | return $this->command->id; 34 | } 35 | 36 | 37 | 38 | public function canExecute() 39 | { 40 | $plugin = BuycraftPlugin::getInstance(); 41 | $player = $plugin->getPlayer(Server::getInstance(), $this->username, $this->xuid); 42 | 43 | if ($this->needOnline) { 44 | if (!$player) { 45 | return false; 46 | } 47 | } 48 | 49 | // Check delay. 50 | if (property_exists($this->command->conditions, "delay")) { 51 | $after = $this->queuedTime + (int)$this->command->conditions->delay; 52 | if (time() < $after) { 53 | return false; 54 | } 55 | } 56 | 57 | // Check inventory slots. 58 | if (property_exists($this->command->conditions, "slots")) { 59 | // Needing inventory slots implies that the player is online, too. 60 | if ($player == NULL) { 61 | return false; 62 | } 63 | 64 | $count = 0; 65 | for ($i = 0; $i < $player->getInventory()->getSize(); $i++) { 66 | if ($player->getInventory()->getItem($i)->getId() === 0) { 67 | $count++; 68 | } 69 | } 70 | 71 | if ($count < (int)$this->command->conditions->slots) { 72 | return false; 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | public function getFinalCommand() 80 | { 81 | $command = str_replace( 82 | [ 83 | '{name}', 84 | '{player}', 85 | '{username}', 86 | '{uuid}', 87 | '{xuid}', 88 | '{id}' 89 | ], 90 | [ 91 | $this->username, 92 | $this->username, 93 | $this->username, 94 | $this->xuid, 95 | $this->xuid, 96 | $this->xuid, 97 | ], 98 | $this->command->command 99 | ); 100 | 101 | return $command; 102 | } 103 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/InventoryUtils.php: -------------------------------------------------------------------------------- 1 | plugin = $main; 19 | } 20 | 21 | public function showCategoryGui(Player $p) 22 | { 23 | $inv = new CategoryInventory(); 24 | 25 | $i = 0; 26 | 27 | foreach ($this->plugin->getCategories() as $category) { 28 | if ($i > 25) { 29 | break; 30 | } 31 | $item = Item::fromString($category['gui_item'] ?? "CHEST"); 32 | 33 | $nbt = $item->getNamedTag() ?? new CompoundTag("", []); 34 | $nbt->setTag(new IntTag("categoryId", $category['id'])); 35 | $item->setNamedTag($nbt); 36 | 37 | $item->setCustomName("§r§f" . $category['name']); 38 | 39 | $inv->addItem($item); 40 | 41 | $i++; 42 | } 43 | 44 | $item = Item::get(Item::WOOL, 5); 45 | $nbt = $item->getNamedTag() ?? new CompoundTag("", []); 46 | $nbt->setTag(new IntTag("buycraft-continue", 1)); 47 | $item->setNamedTag($nbt); 48 | $item->setCustomName("§r§aDrag an item here"); 49 | $inv->setItem(26, $item); 50 | 51 | $p->addWindow($inv); 52 | } 53 | 54 | public function showPackageGui(Player $p, $categoryId) 55 | { 56 | $category = false; 57 | 58 | foreach ($this->plugin->getCategories() as $loopedCategory) { 59 | if ($loopedCategory['id'] === $categoryId) { 60 | $category = $loopedCategory; 61 | } 62 | } 63 | 64 | if (!$category) { 65 | $p->sendMessage("There was a problem loading the packages for this category"); 66 | return false; 67 | } 68 | 69 | $inv = new PackageInventory(); 70 | $currency = $this->plugin->getServerInformation()->account->currency->symbol; 71 | 72 | $i = 0; 73 | foreach ($category['packages'] as $package) { 74 | if ($i > 25) { 75 | break; 76 | } 77 | 78 | $item = Item::fromString($package['gui_item'] ?? "PAPER"); 79 | 80 | $nbt = $item->getNamedTag() ?? new CompoundTag("", []); 81 | $nbt->setTag(new IntTag("packageId", $package['id'])); 82 | $item->setNamedTag($nbt); 83 | 84 | $item->setCustomName("§r§f" . $package['name']); 85 | 86 | $item->setLore([ 87 | "§r§7Price: " . $currency . $package['price'] 88 | ]); 89 | 90 | $inv->addItem($item); 91 | $i++; 92 | } 93 | 94 | $item = Item::get(Item::WOOL, 5); 95 | $nbt = $item->getNamedTag() ?? new CompoundTag("", []); 96 | $nbt->setTag(new IntTag("buycraft-continue", 1)); 97 | $item->setNamedTag($nbt); 98 | $item->setCustomName("§r§aDrag an item here"); 99 | $inv->setItem(26, $item); 100 | 101 | $p->addWindow($inv); 102 | } 103 | 104 | public function getPackageLink(Player $player, $packageId) 105 | { 106 | $request = $this->plugin->getPluginApi()->post("/checkout", [ 107 | "username" => $player->getName(), 108 | "package_id" => $packageId 109 | ]); 110 | 111 | return $request['url']; 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/PackageInventory.php: -------------------------------------------------------------------------------- 1 | holders[$id = $player->getId()])) { 60 | $this->holders[$id] = $this->holder = $player->floor()->add(0, static::INVENTORY_HEIGHT, 0); 61 | $this->sendBlocks($player, self::SEND_BLOCKS_FAKE); 62 | $this->sendFakeTile($player); 63 | parent::onOpen($player); 64 | } 65 | } 66 | 67 | public function onClose(Player $player): void 68 | { 69 | if (isset($this->holders[$id = $player->getId()])) { 70 | parent::onClose($player); 71 | $this->sendBlocks($player, self::SEND_BLOCKS_REAL); 72 | unset($this->holders[$id]); 73 | } 74 | } 75 | 76 | protected function sendFakeTile(Player $player): void 77 | { 78 | $holder = $this->holders[$player->getId()]; 79 | $pk = new BlockEntityDataPacket(); 80 | $pk->x = $holder->x; 81 | $pk->y = $holder->y; 82 | $pk->z = $holder->z; 83 | $tag = new CompoundTag(); 84 | $tag->setString("id", static::FAKE_TILE_ID); 85 | $customName = "Select a package"; 86 | if ($customName !== null) { 87 | $tag->setString("CustomName", $customName); 88 | } 89 | $pk->namedtag = (self::$nbtWriter ?? (self::$nbtWriter = new NetworkLittleEndianNBTStream()))->write($tag); 90 | $player->dataPacket($pk); 91 | } 92 | 93 | protected function sendBlocks(Player $player, int $type): void 94 | { 95 | switch ($type) { 96 | case self::SEND_BLOCKS_FAKE: 97 | $player->getLevel()->sendBlocks([$player], $this->getFakeBlocks($this->holders[$player->getId()])); 98 | return; 99 | case self::SEND_BLOCKS_REAL: 100 | $player->getLevel()->sendBlocks([$player], $this->getRealBlocks($player, $this->holders[$player->getId()])); 101 | return; 102 | } 103 | throw new \Error("Unhandled type $type provided."); 104 | } 105 | 106 | protected function getFakeBlocks(Vector3 $holder): array 107 | { 108 | return [ 109 | Block::get(static::FAKE_BLOCK_ID, static::FAKE_BLOCK_DATA)->setComponents($holder->x, $holder->y, $holder->z) 110 | ]; 111 | } 112 | 113 | protected function getRealBlocks(Player $player, Vector3 $holder): array 114 | { 115 | return [ 116 | $player->getLevel()->getBlockAt($holder->x, $holder->y, $holder->z) 117 | ]; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/CategoryInventory.php: -------------------------------------------------------------------------------- 1 | holders[$id = $player->getId()])) { 60 | $this->holders[$id] = $this->holder = $player->floor()->add(0, static::INVENTORY_HEIGHT, 0); 61 | $this->sendBlocks($player, self::SEND_BLOCKS_FAKE); 62 | $this->sendFakeTile($player); 63 | parent::onOpen($player); 64 | } 65 | } 66 | 67 | public function onClose(Player $player): void 68 | { 69 | if (isset($this->holders[$id = $player->getId()])) { 70 | parent::onClose($player); 71 | $this->sendBlocks($player, self::SEND_BLOCKS_REAL); 72 | unset($this->holders[$id]); 73 | } 74 | } 75 | 76 | protected function sendFakeTile(Player $player): void 77 | { 78 | $holder = $this->holders[$player->getId()]; 79 | $pk = new BlockEntityDataPacket(); 80 | $pk->x = $holder->x; 81 | $pk->y = $holder->y; 82 | $pk->z = $holder->z; 83 | $tag = new CompoundTag(); 84 | $tag->setString("id", static::FAKE_TILE_ID); 85 | $customName = "Select a category"; 86 | if ($customName !== null) { 87 | $tag->setString("CustomName", $customName); 88 | } 89 | $pk->namedtag = (self::$nbtWriter ?? (self::$nbtWriter = new NetworkLittleEndianNBTStream()))->write($tag); 90 | $player->dataPacket($pk); 91 | } 92 | 93 | protected function sendBlocks(Player $player, int $type): void 94 | { 95 | switch ($type) { 96 | case self::SEND_BLOCKS_FAKE: 97 | $player->getLevel()->sendBlocks([$player], $this->getFakeBlocks($this->holders[$player->getId()])); 98 | return; 99 | case self::SEND_BLOCKS_REAL: 100 | $player->getLevel()->sendBlocks([$player], $this->getRealBlocks($player, $this->holders[$player->getId()])); 101 | return; 102 | } 103 | throw new \Error("Unhandled type $type provided."); 104 | } 105 | 106 | protected function getFakeBlocks(Vector3 $holder): array 107 | { 108 | return [ 109 | Block::get(static::FAKE_BLOCK_ID, static::FAKE_BLOCK_DATA)->setComponents($holder->x, $holder->y, $holder->z) 110 | ]; 111 | } 112 | 113 | protected function getRealBlocks(Player $player, Vector3 $holder): array 114 | { 115 | return [ 116 | $player->getLevel()->getBlockAt($holder->x, $holder->y, $holder->z) 117 | ]; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Util/ReportUtil.php: -------------------------------------------------------------------------------- 1 | getPluginApi()->getSecret(); 22 | 23 | $report_lines = []; 24 | $report_lines[] = "### Server Information ###"; 25 | $report_lines[] = "Report generated on " . date('r'); 26 | $report_lines[] = ""; 27 | $report_lines[] = "Operating system: " . PHP_OS . " / " . Utils::getOS(); 28 | $report_lines[] = "PHP version: " . PHP_VERSION; 29 | $report_lines[] = "Server version: " . Server::getInstance()->getPocketMineVersion() . " (API: " . 30 | Server::getInstance()->getApiVersion() . ")"; 31 | 32 | $report_lines[] = ""; 33 | $report_lines[] = "### Platform Information ###"; 34 | $report_lines[] = "Plugin version: " . BuycraftPlugin::getInstance()->getDescription()->getVersion(); 35 | $report_lines[] = ""; 36 | $api_exists = BuycraftPlugin::getInstance()->getPluginApi() !== null; 37 | $report_lines[] = "Connected to Buycraft? " . ($api_exists ? 'yes' : 'no'); 38 | $information = BuycraftPlugin::getInstance()->getServerInformation(); 39 | if ($information !== NULL) { 40 | $report_lines[] = "Web store ID: " . $information->account->id; 41 | $report_lines[] = "Web store URL: " . $information->account->domain; 42 | $report_lines[] = "Web store name: " . $information->account->name; 43 | $report_lines[] = "Web store currency: " . $information->account->currency->iso_4217; 44 | $report_lines[] = "Web store in online mode? " . ($information->account->online_mode ? 'yes' : 'no'); 45 | 46 | $report_lines[] = "Server name: " . $information->server->name; 47 | $report_lines[] = "Server ID: " . $information->server->id; 48 | } 49 | 50 | $report_lines[] = ""; 51 | $report_lines[] = "### Service Status ###"; 52 | return $report_lines; 53 | } 54 | 55 | /** 56 | * Generates the service status lines (this has to be done in an async task for obvious reasons). 57 | * @return array 58 | */ 59 | public static function generateServiceStatus() { 60 | $checks = [ 61 | // Notice that we're not using just plugin.buycraft.net. That's because it throws an error. We'll compromise 62 | // and use the PocketMine versions page. 63 | 'Buycraft plugin API' => [ 64 | "url" => PluginApi::BUYCRAFT_PLUGIN_API_URL . '/versions/pocketmine', 65 | "headers" => [ 66 | "X-Buycraft-Secret" => ReportUtil::$secret 67 | ] 68 | ], 69 | "Google over HTTPS" => 'https://encrypted.google.com', 70 | "Google over HTTP" => 'http://www.google.com' 71 | ]; 72 | 73 | $results = []; 74 | 75 | foreach($checks as $name => $url) { 76 | 77 | if (is_array($url)) { 78 | $ctx = curl_init($url['url']); 79 | 80 | $headers = []; 81 | foreach ($url['headers'] as $k => $v){ 82 | $headers[] = "{$k}: " . $v . ","; 83 | } 84 | curl_setopt($ctx, CURLOPT_HTTPHEADER, [implode(", ", $headers), "User-Agent: BuycraftPM"]); 85 | $url = $url['url']; 86 | } else { 87 | $ctx = curl_init($url); 88 | } 89 | 90 | curl_setopt($ctx, CURLOPT_FAILONERROR, true); 91 | curl_setopt($ctx, CURLOPT_SSL_VERIFYPEER, false); 92 | curl_setopt($ctx, CURLOPT_TIMEOUT, 5); 93 | curl_setopt($ctx, CURLOPT_RETURNTRANSFER, true); 94 | $result = curl_exec($ctx); 95 | if ($result === FALSE) { 96 | $results[] = "Can't access " . $name . " (" . $url . "): " . curl_error($ctx); 97 | } else { 98 | $results[] = "Can access " . $name . " (" . $url . ")"; 99 | } 100 | curl_close($ctx); 101 | } 102 | 103 | return $results; 104 | } 105 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Execution/DuePlayerCheck.php: -------------------------------------------------------------------------------- 1 | pluginApi = $pluginApi; 29 | $this->allowReschedule = $allowReschedule; 30 | } 31 | 32 | /** 33 | * Actions to execute when run 34 | * 35 | * @return void 36 | */ 37 | public function onRun() 38 | { 39 | $page = 1; 40 | $allDue = array(); 41 | 42 | do { 43 | // Sleep for a while between fetches. 44 | if ($page > 1) { 45 | usleep(mt_rand(5, 15) * 100000); 46 | } 47 | 48 | try { 49 | $result = $this->pluginApi->basicGet("/queue?limit=" . self::PLAYERS_PER_PAGE . "&page=" . $page); 50 | } catch (\Exception $e) { 51 | $this->setResult($e); 52 | return; 53 | } 54 | 55 | if (count($result->players) == 0) { 56 | break; 57 | } 58 | 59 | foreach ($result->players as $player) { 60 | $allDue[strtolower($player->name)] = $player; 61 | } 62 | 63 | $page++; 64 | } while ($result->meta->more); 65 | 66 | $this->setResult(array( 67 | 'all_due' => $allDue, 68 | 'next_delay' => $result->meta->next_check ?? self::FALLBACK_DELAY, 69 | 'execute_offline' => $result->meta->execute_offline 70 | )); 71 | } 72 | 73 | public function onCompletion(Server $server) 74 | { 75 | $plugin = BuycraftPlugin::getInstance(); 76 | $result = $this->getResult(); 77 | 78 | // Test if the result is an exception, which indicates something went wrong 79 | if (!($result instanceof \Exception)) { 80 | $plugin->getLogger()->info("Found " . count($result['all_due']) . " due player(s)."); 81 | $plugin->setAllDue($result['all_due']); 82 | 83 | // See if we can execute some commands right now 84 | if ($result['execute_offline']) { 85 | $plugin->getLogger()->info("Executing commands that can be run now..."); 86 | $server->getAsyncPool()->submitTask(new ImmediateExecutionRunner($this->pluginApi)); 87 | } 88 | 89 | // Check for player command execution we can do. 90 | $canProcessNow = array_slice(array_filter($result['all_due'], function ($due) use ($server, $plugin) { 91 | return $plugin->getPlayer($server, $due->name, $due->uuid ? $due->uuid : ""); 92 | }), 0, self::MAXIMUM_ONLINE_PLAYERS_TO_PROCESS); 93 | 94 | if (count($canProcessNow) > 0) { 95 | $plugin->getLogger()->info("Running commands for " . count($canProcessNow) . " online player(s)..."); 96 | 97 | $at = 1; 98 | foreach ($canProcessNow as $due) { 99 | $this->scheduleDelayedAsyncTask(new PlayerCommandExecutor($this->pluginApi, $due), 10 * $at++); 100 | } 101 | } 102 | } else { 103 | $plugin->getLogger()->error("Check failed with message: " . $result->getMessage()); 104 | } 105 | 106 | // Reschedule this task if desired. 107 | if ($this->allowReschedule) { 108 | // PocketMine-MP doesn't allow us to directly delay the eventual execution of an asynchronous task, so 109 | // a workaround must be used. 110 | $nextDelay = is_array($result) ? $result['next_delay'] : self::FALLBACK_DELAY; 111 | $this->scheduleDelayedAsyncTask(new DuePlayerCheck($this->pluginApi, true), $nextDelay * 20); 112 | } 113 | } 114 | 115 | private function scheduleDelayedAsyncTask($task, $delay) 116 | { 117 | BuycraftPlugin::getInstance()->getScheduler()->scheduleDelayedTask(new RunAsyncTask(BuycraftPlugin::getInstance(), $task), $delay); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/PluginApi.php: -------------------------------------------------------------------------------- 1 | secret = $secret; 21 | $this->dataFolder = $dataFolder; 22 | } 23 | 24 | /** 25 | * Returns the decoded JSON response of a simple GET Buycraft API call. 26 | * @param $endpoint string 27 | * @return mixed 28 | * @throws \Exception 29 | */ 30 | public function basicGet($endpoint, $assoc = false, $timeout = 5) 31 | { 32 | // Do a basic GET request 33 | $ctx = $this->initializeCurl(self::BUYCRAFT_PLUGIN_API_URL . $endpoint, $timeout); 34 | $body = curl_exec($ctx); 35 | 36 | // Did the request fail? If so, return an error. 37 | if ($body === FALSE) { 38 | $err = curl_error($ctx); 39 | curl_close($ctx); 40 | 41 | throw new \Exception("cURL request has failed: " . $err); 42 | } 43 | 44 | curl_close($ctx); 45 | 46 | // Try to deserialize the response as JSON. 47 | $result = json_decode($body, $assoc); 48 | 49 | if ($result === NULL) { 50 | throw new \Exception("Result can't be decoded as JSON."); 51 | } 52 | 53 | if ($assoc) { 54 | if (array_key_exists('error_code', $result)) { 55 | throw new \Exception("Error " . $result['error_code'] . ": " . $result['error_message']); 56 | } 57 | } else { 58 | if (property_exists($result, 'error_code')) { 59 | throw new \Exception("Error " . $result->error_code . ": " . $result->error_message); 60 | } 61 | } 62 | 63 | return $result; 64 | } 65 | 66 | public function post($endpoint, $data) 67 | { 68 | $data = json_encode($data); 69 | 70 | $ctx = curl_init(self::BUYCRAFT_PLUGIN_API_URL . $endpoint); 71 | curl_setopt($ctx, CURLOPT_HTTPHEADER, [ 72 | "X-Buycraft-Secret: " . $this->secret, 73 | "User-Agent: BuycraftPM", 74 | "Content-Type: application/json", 75 | "Content-Length: " . strlen($data) 76 | ]); 77 | 78 | curl_setopt($ctx, CURLOPT_SSL_VERIFYPEER, false); 79 | curl_setopt($ctx, CURLOPT_TIMEOUT, 5); 80 | curl_setopt($ctx, CURLOPT_CUSTOMREQUEST, "POST"); 81 | curl_setopt($ctx, CURLOPT_POSTFIELDS, $data); 82 | curl_setopt($ctx, CURLOPT_RETURNTRANSFER, true); 83 | 84 | 85 | $body = curl_exec($ctx); 86 | 87 | if ($body === FALSE) { 88 | $err = curl_error($ctx); 89 | curl_close($ctx); 90 | 91 | throw new \Exception("cURL request has failed: " . $err); 92 | } 93 | 94 | curl_close($ctx); 95 | 96 | $result = json_decode($body, true); 97 | 98 | if ($result === NULL) { 99 | throw new \Exception("Result can't be decoded as JSON."); 100 | } 101 | 102 | if (array_key_exists('error_code', $result)) { 103 | throw new \Exception("Error " . $result['error_code'] . ": " . $result['error_message']); 104 | } 105 | 106 | return $result; 107 | } 108 | 109 | /** 110 | * Returns a cURL session ready to be configured further. This sets the required cURL options for the Buycraft API. 111 | * @param $url string 112 | * @return resource 113 | */ 114 | private function initializeCurl($url, $timeout = 5) 115 | { 116 | $ctx = curl_init($url); 117 | curl_setopt($ctx, CURLOPT_HTTPHEADER, ["X-Buycraft-Secret: " . $this->secret, "User-Agent: BuycraftPM"]); 118 | curl_setopt($ctx, CURLOPT_RETURNTRANSFER, true); 119 | curl_setopt($ctx, CURLOPT_SSL_VERIFYPEER, false); 120 | curl_setopt($ctx, CURLOPT_TIMEOUT, $timeout); 121 | return $ctx; 122 | } 123 | 124 | /** 125 | * Delete the requested commands. 126 | * @param $ids array|integer 127 | * @throws \Exception 128 | */ 129 | public function deleteCommands($ids) 130 | { 131 | if (count($ids) == 0) { 132 | throw new \Exception("Passed ids parameter is not a non-empty array."); 133 | } 134 | 135 | $query = "ids[]=" . implode('&ids[]=', $ids); 136 | $ctx = $this->initializeCurl(self::BUYCRAFT_PLUGIN_API_URL . "/queue"); 137 | curl_setopt($ctx, CURLOPT_FAILONERROR, true); 138 | curl_setopt($ctx, CURLOPT_POST, 1); 139 | curl_setopt($ctx, CURLOPT_CUSTOMREQUEST, "DELETE"); 140 | curl_setopt($ctx, CURLOPT_POSTFIELDS, $query); 141 | $result = curl_exec($ctx); 142 | $err = curl_error($ctx); 143 | curl_close($ctx); 144 | 145 | if ($result === FALSE) { 146 | throw new \Exception("Unable to delete commands: " . $err); 147 | } 148 | } 149 | 150 | public function getSecret() 151 | { 152 | return $this->secret; 153 | } 154 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/BuycraftListener.php: -------------------------------------------------------------------------------- 1 | plugin = $main; 29 | } 30 | 31 | public function onPlayerJoin(PlayerJoinEvent $event) 32 | { 33 | $player = $event->getPlayer(); 34 | if ($this->plugin->isDue($player)) { 35 | $duePlayer = $this->plugin->getDue($player); 36 | $this->plugin->removeDue($player); 37 | 38 | $this->plugin->getLogger()->info("Executing login commands for " . $player->getName() . "[XUID: {$player->getXuid()}]..."); 39 | Server::getInstance()->getAsyncPool()->submitTask(new PlayerCommandExecutor($this->plugin->getPluginApi(), 40 | $duePlayer)); 41 | } 42 | } 43 | 44 | 45 | public function onInventoryTransaction(InventoryTransactionEvent $event): void 46 | { 47 | $tr = $event->getTransaction(); 48 | $actions = $tr->getActions(); 49 | 50 | 51 | foreach ($actions as $action) { 52 | if ($action instanceof SlotChangeAction) { 53 | if ($action->getInventory() instanceof CategoryInventory || 54 | $action->getInventory() instanceof PackageInventory) { 55 | $event->setCancelled(true); 56 | 57 | $target = $action->getTargetItem(); 58 | if ($target->getNamedTag()->hasTag("buycraft-continue", IntTag::class)) { 59 | if ($action->getInventory() instanceof CategoryInventory) { 60 | $event->setCancelled(); 61 | $this->handleCategoryInventoryClick($event, $action); 62 | } elseif 63 | ($action->getInventory() instanceof PackageInventory) { 64 | $event->setCancelled(); 65 | $this->handlePackageInventoryClick($event, $action); 66 | } 67 | } 68 | 69 | } 70 | } 71 | } 72 | } 73 | 74 | 75 | private function handleCategoryInventoryClick(InventoryTransactionEvent $event, SlotChangeAction $action) 76 | { 77 | $item = $action->getSourceItem(); 78 | 79 | if (!$item) { 80 | return false; 81 | } 82 | 83 | $nbt = $item->getNamedTag(); 84 | 85 | if (!$nbt->hasTag("categoryId", IntTag::class)) { 86 | return false; 87 | } 88 | 89 | $p = $event->getTransaction()->getSource(); 90 | 91 | $p->removeWindow($action->getInventory()); 92 | 93 | $inventoryUtils = $this->plugin->getInventoryUtils(); 94 | 95 | $this->plugin->getScheduler()->scheduleDelayedTask(new class($inventoryUtils, $p, $nbt) extends Task 96 | { 97 | 98 | private $inventoryUtils; 99 | private $p; 100 | private $nbt; 101 | 102 | public function __construct($inventoryUtils, $p, $nbt) 103 | { 104 | $this->inventoryUtils = $inventoryUtils; 105 | $this->p = $p; 106 | $this->nbt = $nbt; 107 | } 108 | 109 | function onRun(int $currentTick) 110 | { 111 | $this->inventoryUtils->showPackageGui($this->p, $this->nbt->getInt("categoryId")); 112 | } 113 | 114 | }, 10); 115 | 116 | return true; 117 | } 118 | 119 | private 120 | function handlePackageInventoryClick(InventoryTransactionEvent $event, SlotChangeAction $action) 121 | { 122 | $item = $action->getSourceItem(); 123 | 124 | if (!$item) { 125 | return false; 126 | } 127 | 128 | 129 | $nbt = $item->getNamedTag(); 130 | 131 | if (!$nbt->hasTag("packageId", IntTag::class)) { 132 | return false; 133 | } 134 | 135 | $p = $event->getTransaction()->getSource(); 136 | $packageId = $nbt->getInt("packageId"); 137 | 138 | $p->removeWindow($action->getInventory()); 139 | 140 | $url = $this->plugin->getInventoryUtils()->getPackageLink($p, $packageId); 141 | 142 | $pk = new TextPacket(); 143 | $pk->type = TextPacket::TYPE_RAW; 144 | $pk->message = "§a" . $url; 145 | 146 | $p->sendDataPacket($pk, false, false); 147 | 148 | 149 | return true; 150 | } 151 | } -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/Commands/BuycraftCommand.php: -------------------------------------------------------------------------------- 1 | plugin = $plugin; 27 | } 28 | 29 | /** 30 | * @param CommandSender $sender 31 | * @param string $commandLabel 32 | * @param string[] $args 33 | * 34 | * @return mixed 35 | */ 36 | public function execute(CommandSender $sender, string $commandLabel, array $args) :bool 37 | { 38 | if (!$sender->hasPermission('buycraft.admin')) { 39 | $sender->sendMessage(TextFormat::RED . "You don't have permission to use Buycraft administrative commands."); 40 | return true; 41 | } 42 | 43 | if (count($args) == 0) { 44 | $this->sendHelp($sender); 45 | return true; 46 | } 47 | 48 | switch ($args[0]) { 49 | case "secret": 50 | if (!($sender instanceof ConsoleCommandSender)) { 51 | $sender->sendMessage(TextFormat::RED . "This command must be run from the console."); 52 | return true; 53 | } 54 | 55 | if (count($args) != 2) { 56 | $sender->sendMessage(TextFormat::RED . "This command requires a secret key."); 57 | return true; 58 | } 59 | 60 | $secret = $args[1]; 61 | 62 | $this->plugin->getServer()->getAsyncPool()->submitTask(new SecretVerificationTask($secret, $this->plugin->getDataFolder())); 63 | break; 64 | case "forcecheck": 65 | if (count($args) != 1) { 66 | $sender->sendMessage(TextFormat::RED . "This command doesn't take any arguments."); 67 | return true; 68 | } 69 | 70 | if ($this->plugin->getPluginApi() == null) { 71 | $sender->sendMessage(TextFormat::RED . "You didn't set your secret (or it is invalid). Please set it and try again."); 72 | return true; 73 | } 74 | 75 | $this->plugin->getServer()->getAsyncPool()->submitTask(new DuePlayerCheck($this->plugin->getPluginApi(), false)); 76 | $sender->sendMessage(TextFormat::GREEN . "Force check successfully queued."); 77 | break; 78 | case "info": 79 | if (count($args) != 1) { 80 | $sender->sendMessage(TextFormat::RED . "This command doesn't take any arguments."); 81 | return true; 82 | } 83 | 84 | if ($this->plugin->getServerInformation() == null) { 85 | $sender->sendMessage(TextFormat::RED . "No server information found (did you forget to set your secret?)"); 86 | return true; 87 | } 88 | 89 | $sender->sendMessage(TextFormat::GREEN . "Server " . $this->plugin->getServerInformation()->server->name . " on account " . 90 | $this->plugin->getServerInformation()->account->name); 91 | if (isset($this->plugin->getServerInformation()->game_type)) { 92 | $sender->sendMessage(TextFormat::GREEN . "Web store Type: " 93 | . $this->plugin->getServerInformation()->game_type); 94 | } 95 | $sender->sendMessage(TextFormat::GREEN . "Web store URL: " . $this->plugin->getServerInformation()->account->domain); 96 | $sender->sendMessage(TextFormat::GREEN . "Server currency is " . $this->plugin->getServerInformation()->account->currency->iso_4217); 97 | break; 98 | case "report": 99 | if (!($sender instanceof ConsoleCommandSender)) { 100 | $sender->sendMessage(TextFormat::RED . "This command must be run from the console."); 101 | return true; 102 | } 103 | 104 | $sender->sendMessage(TextFormat::YELLOW . "Generating report, please wait..."); 105 | $lines = ReportUtil::generateBaseReport(); 106 | $this->plugin->getServer()->getAsyncPool()->submitTask(new FinalizeReportTask($lines)); 107 | break; 108 | } 109 | 110 | return true; 111 | } 112 | 113 | private function sendHelp(CommandSender $sender) 114 | { 115 | $sender->sendMessage(TextFormat::GREEN . "Usage for the Tebex-PMMP plugin:"); 116 | $sender->sendMessage(TextFormat::GREEN . "/tebex:secret" . TextFormat::GRAY . ": Set your server's secret."); 117 | $sender->sendMessage(TextFormat::GREEN . "/tebex:forcecheck" . TextFormat::GRAY . ": Check for current purchases."); 118 | $sender->sendMessage(TextFormat::GREEN . "/tebex:info" . TextFormat::GRAY . ": Retrieves public information about your web store."); 119 | $sender->sendMessage(TextFormat::GREEN . "/tebex:report" . TextFormat::GRAY . ": Generates a report you can send to Buycraft support."); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Buycraft/PocketMine/BuycraftPlugin.php: -------------------------------------------------------------------------------- 1 | getLogger()->error("Tebex-PMMP requires the curl extension to be installed with SSL support. Halting..."); 47 | return; 48 | } 49 | 50 | $version = curl_version(); 51 | $ssl_supported = ($version['features'] & CURL_VERSION_SSL); 52 | if (!$ssl_supported) 53 | { 54 | $this->getLogger()->error("Tebex-PMMP requires the curl extension to be installed with SSL support. Halting..."); 55 | return; 56 | } 57 | 58 | self::$instance = $this; 59 | 60 | $this->saveDefaultConfig(); 61 | 62 | $secret = $this->getConfig()->get('secret'); 63 | if ($secret) { 64 | $api = new PluginApi($secret, $this->getDataFolder()); 65 | $this->inventoryUtils = new InventoryUtils($this); 66 | try { 67 | $this->verifyInformation($api); 68 | $this->pluginApi = $api; 69 | $this->startInitialTasks(); 70 | } catch (\Exception $e) { 71 | $this->getLogger()->warning("Unable to verify information"); 72 | //$this->getLogger()->logException($e); 73 | } 74 | } else { 75 | 76 | //Can we migrate? 77 | if(file_exists($this->getDataFolder() . "../BuycraftPM/config.yml")){ 78 | $oldconfig = new Config($this->getDataFolder() . "../BuycraftPM/config.yml",Config::YAML); 79 | if ($oldconfig->get("secret")) { 80 | $this->getLogger()->info("Migrating secret from old BuycraftPM plugin..."); 81 | $this->getServer()->getAsyncPool()->submitTask( 82 | new SecretVerificationTask($oldconfig->get("secret"), $this->getDataFolder()) 83 | ); 84 | } 85 | } else { 86 | 87 | $this->getLogger() 88 | ->info("Looks like this is your first time using Tebex. Set up your server by using 'tebex secret '."); 89 | } 90 | } 91 | 92 | $this->getServer()->getPluginManager()->registerEvents(new BuycraftListener($this), $this); 93 | $this->getServer()->getCommandMap()->register("buycraft", new BuycraftCommandAlias($this, "buycraft")); 94 | $this->getServer()->getCommandMap()->register("tebex", new BuycraftCommand($this, "tebex")); 95 | $this->getServer()->getCommandMap()->register("buy", new BuyCommand($this)); 96 | } 97 | 98 | private function verifyInformation(PluginApi $api) 99 | { 100 | try { 101 | $this->serverInformation = $api->basicGet("/information"); 102 | } catch (\Exception $e) { 103 | $this->getLogger()->warning("Unable to verify information"); 104 | //$this->getLogger()->logException($e); 105 | } 106 | } 107 | 108 | private function startInitialTasks() 109 | { 110 | $this->commandExecutionTask = new CommandExecutor(); 111 | $this->getScheduler()->scheduleRepeatingTask($this->commandExecutionTask, 1); 112 | $this->deleteCommandsTask = new DeleteCommandsTask($this->pluginApi); 113 | $this->getScheduler()->scheduleRepeatingTask($this->deleteCommandsTask, 20); 114 | $this->categoryRefreshTask = new CategoryRefreshTask($this); 115 | $this->getScheduler()->scheduleRepeatingTask($this->categoryRefreshTask, 20 * 60 * 3); 116 | $this->getServer()->getAsyncPool()->submitTask(new DuePlayerCheck($this->pluginApi, true)); 117 | } 118 | 119 | public function onDisable() 120 | { 121 | $this->saveConfig(); 122 | } 123 | 124 | /** 125 | * @return PluginApi 126 | */ 127 | public function getPluginApi() 128 | { 129 | return $this->pluginApi; 130 | } 131 | 132 | /** 133 | * @return CommandExecutor 134 | */ 135 | public function getCommandExecutionTask() 136 | { 137 | return $this->commandExecutionTask; 138 | } 139 | 140 | /** 141 | * @return DeleteCommandsTask 142 | */ 143 | public function getDeleteCommandsTask() 144 | { 145 | return $this->deleteCommandsTask; 146 | } 147 | 148 | /** 149 | * @return bool 150 | */ 151 | public function isDue(Player $player): bool 152 | { 153 | return isset($this->allDue[$player->getLowerCaseName()]); 154 | } 155 | 156 | /** 157 | * @return object 158 | */ 159 | public function getDue(Player $player) 160 | { 161 | return $this->allDue[$player->getLowerCaseName()]; 162 | } 163 | 164 | public function removeDue(Player $player): void 165 | { 166 | unset($this->allDue[$player->getLowerCaseName()]); 167 | } 168 | 169 | /** 170 | * @param array $allDue 171 | */ 172 | public function setAllDue(array $allDue) 173 | { 174 | $this->allDue = $allDue; 175 | } 176 | 177 | public function getPlayer(Server $server, $username, $xuid = '') 178 | { 179 | if ($xuid != '') { 180 | $this->getLogger()->info("Checking for existing of player with XUID {$xuid}"); 181 | foreach ($server->getOnlinePlayers() as $player) { 182 | if ($player->getXuid() === $xuid) { 183 | return $player; 184 | } 185 | } 186 | 187 | return false; 188 | } 189 | 190 | $this->getLogger()->info("Checking for existing of player with Username {$username}"); 191 | 192 | $player = $server->getPlayerExact($username); 193 | 194 | return $player ? $player : false; 195 | } 196 | 197 | /** 198 | * Attempts to change the current API object. Will not always work, but due to the "design" of threaded PHP, this 199 | * is the only way we can accomplish this. 200 | * @param $newApi PluginApi 201 | * @param $information mixed 202 | */ 203 | public function changeApi(PluginApi $newApi, $information) 204 | { 205 | $this->pluginApi = $newApi; 206 | $this->getScheduler()->cancelAllTasks(); 207 | $this->startInitialTasks(); 208 | 209 | // change information if required (for secret command) 210 | if ($information !== NULL) { 211 | $this->serverInformation = $information; 212 | } 213 | } 214 | 215 | /** 216 | * @return mixed 217 | */ 218 | public function getServerInformation() 219 | { 220 | return $this->serverInformation; 221 | } 222 | 223 | public function setCategories(array $categories) 224 | { 225 | $this->categories = $categories; 226 | } 227 | 228 | public function getCategories() 229 | { 230 | return $this->categories; 231 | } 232 | 233 | public function getInventoryUtils() 234 | { 235 | return $this->inventoryUtils; 236 | } 237 | } --------------------------------------------------------------------------------