-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated the dev version for pmmp4, this is a FINAL relase
- Loading branch information
1 parent
37b9279
commit 3ebe3f7
Showing
52 changed files
with
2,251 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.