Skip to content

Commit

Permalink
Updated the dev version for pmmp4, this is a FINAL relase
Browse files Browse the repository at this point in the history
  • Loading branch information
FoxWorn3365 committed Jun 26, 2023
1 parent 37b9279 commit 3ebe3f7
Show file tree
Hide file tree
Showing 52 changed files with 2,251 additions and 4 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</p>

---
<h1 align="center">Shopkeepers v0.9.1 for PocketMine-MP 5</h1>
<h1 align="center">Shopkeepers v0.9.1 for PocketMine-MP 4</h1>
<br>

**⚠️ We are not in any way related to the [Shopkeepers plugin](https://dev.bukkit.org/projects/shopkeepers) for Bukkit!**
Expand All @@ -35,8 +35,8 @@
## Compatibility
**Shopkeepers** is made to be multi-version, in fact I announce with great joy that the plugin is available for both PocketMine-MP 5 and PocketMine-MP 4!
> **Warning**<br>
> The Shopkeepers version for PocketMine-MP 4 is available exclusively here on GitHub since InvMenu has versions that are not compatible with each other!
> The branch can be found [here](https://github.com/FoxWorn3365/Shopkeepers/tree/pmmp4)
> This is the branch for **PocketMine-MP 4** only!<br>
> The branch for PocketMine-MP **5** can be found [here](https://github.com/FoxWorn3365/Shopkeepers) or on [poggit]()
## Configuration
The configuration of **Shopkeepers** allows you to customize some values to make it suitable for all servers.
Expand Down
2 changes: 1 addition & 1 deletion plugin.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Shopkeepers
version: 0.9.1
api: 5.0.0
api: 4.0.0

main: FoxWorn3365\Shopkeepers\Core
author: FoxWorn3365
Expand Down
178 changes: 178 additions & 0 deletions src/muqsit/invmenu/InvMenu.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

declare(strict_types=1);

namespace muqsit\invmenu;

use Closure;
use LogicException;
use muqsit\invmenu\inventory\SharedInvMenuSynchronizer;
use muqsit\invmenu\session\InvMenuInfo;
use muqsit\invmenu\session\network\PlayerNetwork;
use muqsit\invmenu\transaction\DeterministicInvMenuTransaction;
use muqsit\invmenu\transaction\InvMenuTransaction;
use muqsit\invmenu\transaction\InvMenuTransactionResult;
use muqsit\invmenu\transaction\SimpleInvMenuTransaction;
use muqsit\invmenu\type\InvMenuType;
use muqsit\invmenu\type\InvMenuTypeIds;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\item\Item;
use pocketmine\player\Player;

class InvMenu implements InvMenuTypeIds{

/**
* @param string $identifier
* @param mixed ...$args
* @return InvMenu
*/
public static function create(string $identifier, ...$args) : InvMenu{
return new InvMenu(InvMenuHandler::getTypeRegistry()->get($identifier), ...$args);
}

/**
* @param (Closure(DeterministicInvMenuTransaction) : void)|null $listener
* @return Closure(InvMenuTransaction) : InvMenuTransactionResult
*/
public static function readonly(?Closure $listener = null) : Closure{
return static function(InvMenuTransaction $transaction) use($listener) : InvMenuTransactionResult{
$result = $transaction->discard();
if($listener !== null){
$listener(new DeterministicInvMenuTransaction($transaction, $result));
}
return $result;
};
}

protected InvMenuType $type;
protected ?string $name = null;
protected ?Closure $listener = null;
protected ?Closure $inventory_close_listener = null;
protected Inventory $inventory;
protected ?SharedInvMenuSynchronizer $synchronizer = null;

public function __construct(InvMenuType $type, ?Inventory $custom_inventory = null){
if(!InvMenuHandler::isRegistered()){
throw new LogicException("Tried creating menu before calling " . InvMenuHandler::class . "::register()");
}
$this->type = $type;
$this->inventory = $this->type->createInventory();
$this->setInventory($custom_inventory);
}

public function getType() : InvMenuType{
return $this->type;
}

public function getName() : ?string{
return $this->name;
}

public function setName(?string $name) : self{
$this->name = $name;
return $this;
}

/**
* @param (Closure(InvMenuTransaction) : InvMenuTransactionResult)|null $listener
* @return self
*/
public function setListener(?Closure $listener) : self{
$this->listener = $listener;
return $this;
}

/**
* @param (Closure(Player, Inventory) : void)|null $listener
* @return self
*/
public function setInventoryCloseListener(?Closure $listener) : self{
$this->inventory_close_listener = $listener;
return $this;
}

/**
* @param Player $player
* @param string|null $name
* @param (Closure(bool) : void)|null $callback
*/
final public function send(Player $player, ?string $name = null, ?Closure $callback = null) : void{
$player->removeCurrentWindow();

$session = InvMenuHandler::getPlayerManager()->get($player);
$network = $session->getNetwork();

// Avoid players from spamming InvMenu::send() and other similar
// requests and filling up queued tasks in memory.
// It would be better if this check were implemented by plugins,
// however I suppose it is more convenient if done within InvMenu...
if($network->getPending() >= 8){
$network->dropPending();
}else{
$network->dropPendingOfType(PlayerNetwork::DELAY_TYPE_OPERATION);
}

$network->waitUntil(PlayerNetwork::DELAY_TYPE_OPERATION, 0, function(bool $success) use($player, $session, $name, $callback) : bool{
if(!$success){
if($callback !== null){
$callback(false);
}
return false;
}

$graphic = $this->type->createGraphic($this, $player);
if($graphic !== null){
$session->setCurrentMenu(new InvMenuInfo($this, $graphic, $name), static function(bool $success) use($callback) : void{
if($callback !== null){
$callback($success);
}
});
}else{
if($callback !== null){
$callback(false);
}
}
return false;
});
}

public function getInventory() : Inventory{
return $this->inventory;
}

public function setInventory(?Inventory $custom_inventory) : void{
if($this->synchronizer !== null){
$this->synchronizer->destroy();
$this->synchronizer = null;
}

if($custom_inventory !== null){
$this->synchronizer = new SharedInvMenuSynchronizer($this, $custom_inventory);
}
}

/**
* @internal use InvMenu::send() instead.
*
* @param Player $player
* @return bool
*/
public function sendInventory(Player $player) : bool{
return $player->setCurrentWindow($this->getInventory());
}

public function handleInventoryTransaction(Player $player, Item $out, Item $in, SlotChangeAction $action, InventoryTransaction $transaction) : InvMenuTransactionResult{
$inv_menu_txn = new SimpleInvMenuTransaction($player, $out, $in, $action, $transaction);
return $this->listener !== null ? ($this->listener)($inv_menu_txn) : $inv_menu_txn->continue();
}

public function onClose(Player $player) : void{
if($this->inventory_close_listener !== null){
($this->inventory_close_listener)($player, $this->getInventory());
}

InvMenuHandler::getPlayerManager()->get($player)->removeCurrentMenu();
}
}
97 changes: 97 additions & 0 deletions src/muqsit/invmenu/InvMenuEventHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace muqsit\invmenu;

use muqsit\invmenu\session\network\PlayerNetwork;
use muqsit\invmenu\session\PlayerManager;
use pocketmine\event\inventory\InventoryCloseEvent;
use pocketmine\event\inventory\InventoryTransactionEvent;
use pocketmine\event\Listener;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;

final class InvMenuEventHandler implements Listener{

public function __construct(
private PlayerManager $player_manager
){}

/**
* @param DataPacketReceiveEvent $event
* @priority NORMAL
*/
public function onDataPacketReceive(DataPacketReceiveEvent $event) : void{
$packet = $event->getPacket();
if($packet instanceof NetworkStackLatencyPacket){
$player = $event->getOrigin()->getPlayer();
if($player !== null){
$this->player_manager->getNullable($player)?->getNetwork()->notify($packet->timestamp);
}
}
}

/**
* @param InventoryCloseEvent $event
* @priority MONITOR
*/
public function onInventoryClose(InventoryCloseEvent $event) : void{
$player = $event->getPlayer();
$session = $this->player_manager->getNullable($player);
if($session === null){
return;
}

$current = $session->getCurrent();
if($current !== null && $event->getInventory() === $current->menu->getInventory()){
$current->menu->onClose($player);
}
$session->getNetwork()->waitUntil(PlayerNetwork::DELAY_TYPE_ANIMATION_WAIT, 325, static fn(bool $success) : bool => false);
}

/**
* @param InventoryTransactionEvent $event
* @priority NORMAL
*/
public function onInventoryTransaction(InventoryTransactionEvent $event) : void{
$transaction = $event->getTransaction();
$player = $transaction->getSource();

$player_instance = $this->player_manager->get($player);
$current = $player_instance->getCurrent();
if($current === null){
return;
}

$inventory = $current->menu->getInventory();
$network_stack_callbacks = [];
foreach($transaction->getActions() as $action){
if(!($action instanceof SlotChangeAction) || $action->getInventory() !== $inventory){
continue;
}

$result = $current->menu->handleInventoryTransaction($player, $action->getSourceItem(), $action->getTargetItem(), $action, $transaction);
$network_stack_callback = $result->getPostTransactionCallback();
if($network_stack_callback !== null){
$network_stack_callbacks[] = $network_stack_callback;
}
if($result->isCancelled()){
$event->cancel();
break;
}
}

if(count($network_stack_callbacks) > 0){
$player_instance->getNetwork()->wait(PlayerNetwork::DELAY_TYPE_ANIMATION_WAIT, static function(bool $success) use($player, $network_stack_callbacks) : bool{
if($success){
foreach($network_stack_callbacks as $callback){
$callback($player);
}
}
return false;
});
}
}
}
46 changes: 46 additions & 0 deletions src/muqsit/invmenu/InvMenuHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace muqsit\invmenu;

use InvalidArgumentException;
use LogicException;
use muqsit\invmenu\session\PlayerManager;
use muqsit\invmenu\type\InvMenuTypeRegistry;
use pocketmine\plugin\Plugin;
use pocketmine\Server;

final class InvMenuHandler{

private static ?Plugin $registrant = null;
private static InvMenuTypeRegistry $type_registry;
private static PlayerManager $player_manager;

public static function register(Plugin $plugin) : void{
if(self::isRegistered()){
throw new InvalidArgumentException("{$plugin->getName()} attempted to register " . self::class . " twice.");
}

self::$registrant = $plugin;
self::$type_registry = new InvMenuTypeRegistry();
self::$player_manager = new PlayerManager(self::getRegistrant());
Server::getInstance()->getPluginManager()->registerEvents(new InvMenuEventHandler(self::getPlayerManager()), $plugin);
}

public static function isRegistered() : bool{
return self::$registrant instanceof Plugin;
}

public static function getRegistrant() : Plugin{
return self::$registrant ?? throw new LogicException("Cannot obtain registrant before registration");
}

public static function getTypeRegistry() : InvMenuTypeRegistry{
return self::$type_registry;
}

public static function getPlayerManager() : PlayerManager{
return self::$player_manager;
}
}
23 changes: 23 additions & 0 deletions src/muqsit/invmenu/inventory/InvMenuInventory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace muqsit\invmenu\inventory;

use pocketmine\block\inventory\BlockInventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;

final class InvMenuInventory extends SimpleInventory implements BlockInventory{

private Position $holder;

public function __construct(int $size){
parent::__construct($size);
$this->holder = new Position(0, 0, 0, null);
}

public function getHolder() : Position{
return $this->holder;
}
}
Loading

0 comments on commit 3ebe3f7

Please sign in to comment.