Skip to content

Commit 7b01d38

Browse files
committed
Merge remote-tracking branch 'upstream/pm5' into pm5
2 parents 68accaf + ab3ab2d commit 7b01d38

File tree

10 files changed

+95
-152
lines changed

10 files changed

+95
-152
lines changed

README.md

Lines changed: 86 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
11
# InvMenu
2-
**InvMenu is a PocketMine-MP virion that eases creating and managing fake inventories!**
3-
[![](https://poggit.pmmp.io/shield.state/InvMenu)](https://poggit.pmmp.io/p/InvMenu)
2+
Create and manage virtual inventories in PocketMine-MP.
43

5-
## Installation
6-
You can get the compiled .phar file on poggit by clicking [here](https://poggit.pmmp.io/ci/Muqsit/InvMenu/~).
4+
## Installation and setup
5+
Download the compiled .phar file from [Poggit CI](https://poggit.pmmp.io/ci/Muqsit/InvMenu/~) and place it in your `virions/` folder.
6+
Read [installation](https://github.com/Muqsit/InvMenu/wiki/Installation) and [using in a plugin](https://github.com/Muqsit/InvMenu/wiki/Using-InvMenu-in-a-plugin)
7+
for a more elaborate guide on how to setup InvMenu library.
78

8-
## Usage
9-
InvMenu supports creating a GUI out of any kind of `Inventory`.
9+
> [!NOTE]
10+
> You must register `InvMenuHandler` before you can use InvMenu.
11+
> ```php
12+
> // in class MyPlugin extends PluginBase:
13+
> protected function onEnable() : void{
14+
> if(!InvMenuHandler::isRegistered()){
15+
> InvMenuHandler::register($this);
16+
> }
17+
> }
1018
11-
**NOTE:** You MUST register `InvMenuHandler` during plugin enable before you can begin creating `InvMenu` instances.
12-
```php
13-
if(!InvMenuHandler::isRegistered()){
14-
InvMenuHandler::register($this);
15-
}
16-
```
19+
## Create a virtual inventory
20+
Quick start, use `InvMenu::create(InvMenu::TYPE_CHEST)->send($player);` to display a virtual chest inventory to a player.
1721
18-
## Creating an InvMenu instance
19-
`InvMenu::create($identifier)` creates a new instance of InvMenu. `$identifier` must be an identifier of a registered `InvMenuType` object. InvMenu comes with 3 pre-registered `InvMenuType` identifiers: `InvMenu::TYPE_CHEST`, `InvMenu::TYPE_DOUBLE_CHEST` and `InvMenu::TYPE_HOPPER`.
22+
`InvMenu::create($identifier)` creates an InvMenu instance. `$identifier` may be an identifier of a registered `InvMenuType` object.
23+
InvMenu comes with 3 pre-registered inventory types of different sizes:
24+
- `InvMenu::TYPE_CHEST` - a 27-slot normal chest inventory
25+
- `InvMenu::TYPE_DOUBLE_CHEST` - a 54-slot double chest inventory
26+
- `InvMenu::TYPE_HOPPER` - a 5-slot hopper inventory
2027
2128
```php
2229
$menu = InvMenu::create(InvMenu::TYPE_CHEST);
23-
```
24-
25-
To access this menu's inventory, you can use:
26-
```php
2730
$inventory = $menu->getInventory();
2831
```
2932
30-
The `$inventory` implements pocketmine's `Inventory` interface, so you can access all the fancy pocketmine inventory methods.
33+
As `$inventory` implements [PocketMine's Inventory interface](https://github.com/pmmp/PocketMine-MP/blob/stable/src/inventory/Inventory.php), you get to access all the fancy PocketMine inventory methods.
3134
```php
3235
$menu->getInventory()->setContents([
3336
VanillaItems::DIAMOND_SWORD(),
@@ -36,113 +39,102 @@ $menu->getInventory()->setContents([
3639
$menu->getInventory()->addItem(VanillaItems::DIAMOND_AXE());
3740
$menu->getInventory()->setItem(3, VanillaItems::GOLD_INGOT());
3841
```
39-
To send the menu to a player, use:
42+
To send a menu to a player, use:
4043
```php
4144
/** @var Player $player */
4245
$menu->send($player);
4346
```
44-
Yup, that's it. It's that simple.
47+
> [!TIP]
48+
> One `InvMenu` can be sent to multiple players—even 2 players in different worlds, so everyone views and edits the same inventory as if it were one chest.
4549
46-
## Specifying a custom name to the menu
47-
To set a custom name to a menu, use
48-
```php
49-
$menu->setName("Custom Name");
50-
```
51-
You can also specify a different menu name for each player separately during `InvMenu::send()`.
50+
51+
## Set a custom name
52+
There are two ways to name an InvMenu. You can either specify a global name (see method A), or you can set a name at the time you send the menu (see method B).
5253
```php
53-
/** @var Player $player */
54-
$menu->send($player, "Greetings, " . $player->getName());
54+
$menu->setName("Custom Name"); // method A
55+
$menu->send($player, "Greetings, " . $player->getName()); // method B
5556
```
5657

57-
## Verifying whether the menu was sent to the player
58-
Not a common occurrence but it's possible for plugins to disallow players from opening inventories.
59-
This can also occur as an attempt to drop garbage `InvMenu::send()` requests (if you send two menus simultaneously without any delay in betweeen, the first menu request may be regarded as garbage).
58+
## Verify whether a menu is sent successfully
59+
`InvMenu::send()` is not guaranteed to succeed. A failure may arise from plugins cancelling InventoryOpenEvent, a disconnected player, or the player refusing the request (e.g., because they are in pause menu).
60+
Use the `$callback` parameter to verify whether a menu has been opened.
6061
```php
61-
/** @var string|null $name */
62-
$menu->send($player, $name, function(bool $sent) : void{
63-
if($sent){
64-
// do something
62+
$menu->send($player, callback: function(bool $success) : void{
63+
if($success){
64+
// player is viewing the menu
6565
}
6666
});
6767
```
6868

69-
## Handling menu item transactions
70-
To handle item transactions happening to and from the menu's inventory, you may specify a `Closure` handler that gets triggered by `InvMenu` every time a transaction occurs. You may allow, cancel and do other things within this handler. To register a transaction handler to a menu, use:
71-
```php
72-
/** @var Closure $listener */
73-
$menu->setListener($listener);
74-
```
75-
What's **`$listener`**?
69+
## Monitor movement of items
70+
InvMenu comes with a listener whereby developers can write logic to monitor movement of items in and out of inventory, and thereby take action.
71+
A listener is a callback with the following signature:
7672
```php
7773
/**
7874
* @param InvMenuTransaction $transaction
7975
*
80-
* Must return an InvMenuTransactionResult instance.
8176
* Return $transaction->continue() to continue the transaction.
8277
* Return $transaction->discard() to cancel the transaction.
8378
* @return InvMenuTransactionResult
8479
*/
8580
Closure(InvMenuTransaction $transaction) : InvMenuTransactionResult;
8681
```
87-
`InvMenuTransaction` holds all the item transction data.<br>
88-
`InvMenuTransaction::getPlayer()` returns the `Player` that triggered the transaction.<br>
89-
`InvMenuTransaction::getItemClicked()` returns the `Item` the player clicked in the menu.<br>
90-
`InvMenuTransaction::getItemClickedWith()` returns the `Item` the player had in their hand when clicking an item.<br>
91-
`InvMenuTransaction::getAction()` returns a `SlotChangeAction` instance, to get the slot index of the item clicked from the menu's inventory.<br>
92-
`InvMenuTransaction::getTransaction()` returns the complete `InventoryTransaction` instance.<br>
82+
- `InvMenuTransaction::getPlayer()` returns the `Player` that triggered the transaction.
83+
- `InvMenuTransaction::getItemClicked()` returns the `Item` the player clicked in the menu. You may also use `InvMenuTransaction::getOut()`.
84+
- `InvMenuTransaction::getItemClickedWith()` returns the `Item` the player had in their hand when clicking an item. You may also use `InvMenuTransaction::getIn()`.
85+
- `InvMenuTransaction::getAction()` returns `SlotChangeAction` - you can get the slot that the player clicked in the menu.
86+
- `InvMenuTransaction::getTransaction()` returns the complete `InventoryTransaction` holding all the above information.
9387
```php
9488
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
9589
$player = $transaction->getPlayer();
9690
$itemClicked = $transaction->getItemClicked();
9791
$itemClickedWith = $transaction->getItemClickedWith();
9892
$action = $transaction->getAction();
99-
$invTransaction = $transaction->getTransaction();
93+
$txn = $transaction->getTransaction();
10094
return $transaction->continue();
10195
});
10296
```
103-
A handler that doesn't allow players to take out apples from the menu's inventory:
97+
The listener below does not allow players to take out apples from the menu:
10498
```php
10599
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
106-
if($transaction->getItemClicked()->getId() === ItemIds::APPLE){
100+
if($transaction->getItemClicked()->getTypeId() === ItemTypeIds::APPLE){
107101
$player->sendMessage("You cannot take apples out of that inventory.");
108102
return $transaction->discard();
109103
}
110104
return $transaction->continue();
111105
});
112106
```
113107

114-
### Preventing inventory from being changed by players
115-
There are two ways you can go with to prevent players from modifying the inventory contents of a menu.
116-
#### Method #1: Calling `InvMenuTransaction::discard()`
108+
There are two methods you can use to prevent players from editing the menu. Either create a listener that `discard()`s
109+
the transaction, or use `InvMenu::readonly()`.
117110
```php
118111
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
119-
// do something
120112
return $transaction->discard();
121113
});
122-
```
123-
#### Method #2: Using `InvMenu::readonly()`
124-
```php
125-
$menu->setListener(InvMenu::readonly());
126-
```
127-
```php
114+
115+
$menu->setListener(InvMenu::readonly()); // equivalent shorthand of the above
116+
117+
// you can also pass a callback in InvMenu::readonly()
128118
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
129119
// do something
130120
}));
131121
```
132-
Based on your use-case, you may find one better than the other. While `Method #1` gives you full control over a transaction (you can conditionally cancel a transaction, f.e based on whether player has permission, or player is in a specific area etc), `Method #2` reduces boilerplate `InvMenuTransactionResult` imports and calls to `InvMenutransaction::discard()`.
122+
Alternatively, you may choose to write your own `InventoryTransactionEvent` listener that works on transactions on
123+
`$menu->getInventory()`. However, an InvMenu listener is enough to fulfil most tasks.
133124

134-
## Executing a task post-transaction
135-
A few actions are impossible to be done at the time a player is viewing an inventory, such as sending a form — a player won't be able to view a form while viewing an inventory. To do this, you will need to close the menu inventory and make sure they've closed it by waiting for a response from their side. You can do this by supplying a callback to `InvMenuTransactionResult::then()`.
125+
## Execute a task post-transaction
126+
Few actions are not possible to invoke at the time a player is viewing an inventory, such as sending a form—a player
127+
cannot view a form while viewing an inventory. Close the menu and utilize `InvMenuTransactionResult::then()` callback to
128+
achieve this.
136129
```php
137130
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
138131
$transaction->getPlayer()->removeCurrentWindow();
139-
return $transaction->discard()->then(function(Player $player) : void{ // $player === $transaction->getPlayer()
140-
// assert($player->isOnline());
132+
return $transaction->discard()->then(function(Player $player) : void{
141133
$player->sendForm(new Form());
142134
});
143135
});
144-
```
145-
```php
136+
137+
// or if you are using InvMenu::readonly():
146138
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
147139
$transaction->getPlayer()->removeCurrentWindow();
148140
$transaction->then(function(Player $player) : void{
@@ -151,42 +143,48 @@ $menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $t
151143
}));
152144
```
153145

154-
## Listening players closing or no longer viewing the inventory
155-
To listen inventory close triggers, specify the inventory close Closure using:
156-
```php
157-
/** @var Closure $listener */
158-
$menu->setInventoryCloseListener($listener);
159-
```
160-
What's **`$listener`**?
146+
## Monitor menu close events
147+
Register an inventory close callback to run whenever a player closes the menu. An inventory close callback takes the
148+
following signature:
161149
```php
162150
/**
163-
* @param Player $player the player who closed the inventory.
164-
*
165-
* @param Inventory $inventory the inventory instance closed by the player.
151+
* @param Player $player the player that closed the menu
152+
* @param Inventory $inventory the inventory of the menu
166153
*/
167154
Closure(Player $player, Inventory $inventory) : void;
168155
```
169-
To forcefully close or remove the menu from a player:
170156
```php
171-
/** @var Player $player */
172-
$player->removeCurrentWindow();
157+
$menu->setInventoryCloseListener(function(Player $player, Inventory $inventory) : void{
158+
$player->sendMessage("You are no longer viewing the menu.");
159+
});
173160
```
161+
Inventory close listener is fired during both—server-initiated requests (i.e., `$player->removeCurrentWindow()`) and
162+
when the player closes the inventory on their end.
174163

175-
## Registering a custom InvMenu type
176-
So let's say you'd like to send players a dispenser inventory. While InvMenu doesn't ship with a `InvMenu::TYPE_DISPENSER`, you can still create a dispenser InvMenu by registering an `InvMenuType` object with the information about what a dispenser inventory looks like.
164+
## Advanced usage: Register a custom InvMenuType
165+
> [!IMPORTANT]
166+
> PocketMine does not register a dispenser block. As of PocketMine v5, the task of registering missing vanilla blocks is
167+
> excessively laborious and hence beyond the scope of this guide. [pmmp/RegisterBlocksDemoPM5](https://github.com/pmmp/RegisterBlocksDemoPM5)
168+
> has a nice guide on how to achieve this. **Still overwhelmed?** I wrote a [drag-n-drop example plugin](https://gist.github.com/Muqsit/8884e0f75b317c332a56e01740bbfe98)
169+
> that does all of it and registers a `/dispenser` command. With DevTools plugin installed, simply copy the code and
170+
> paste it in a new "DispenserInvMenuPlugin.php" file in your server's plugin folder.
171+
172+
InvMenu does not provide a 9-slot dispenser inventory. But you can still achieve this by registering a dispenser InvMenuType.
173+
You'll need to specify inventory size, block actor identifier (tile identifier), and the window type (network property) for
174+
the creation of the graphic (block) and inventory parts.
177175
```php
178176
public const TYPE_DISPENSER = "myplugin:dispenser";
179177

180178
protected function onEnable() : void{
181179
InvMenuHandler::getTypeRegistry()->register(self::TYPE_DISPENSER, InvMenuTypeBuilders::BLOCK_ACTOR_FIXED()
182-
->setBlock(BlockFactory::getInstance()->get(BlockLegacyIds::DISPENSER, 0))
183-
->setBlockActorId("Dispenser")
180+
->setBlock(ExtraVanillaBlocks::DISPENSER())
184181
->setSize(9)
182+
->setBlockActorId("Dispenser")
185183
->setNetworkWindowType(WindowTypes::DISPENSER)
186184
->build());
187185
}
188186
```
189-
Sweet! Now you can create a dispenser menu using
187+
Sweet! Now you can create a dispenser menu using:
190188
```php
191189
$menu = InvMenu::create(self::TYPE_DISPENSER);
192190
```

src/muqsit/invmenu/InvMenu.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ public static function readonly(?Closure $listener = null) : Closure{
5454
protected ?SharedInvMenuSynchronizer $synchronizer = null;
5555

5656
public function __construct(InvMenuType $type, ?Inventory $custom_inventory = null){
57-
if(!InvMenuHandler::isRegistered()){
58-
throw new LogicException("Tried creating menu before calling " . InvMenuHandler::class . "::register()");
59-
}
57+
InvMenuHandler::isRegistered() || throw new LogicException("Tried creating menu before calling " . InvMenuHandler::class . "::register()");
6058
$this->type = $type;
6159
$this->inventory = $this->type->createInventory();
6260
$this->setInventory($custom_inventory);
@@ -66,14 +64,6 @@ public function __destruct(){
6664
$this->setInventory(null);
6765
}
6866

69-
/**
70-
* @deprecated Access {@see InvMenu::$type} directly
71-
* @return InvMenuType
72-
*/
73-
public function getType() : InvMenuType{
74-
return $this->type;
75-
}
76-
7767
public function getName() : ?string{
7868
return $this->name;
7969
}

src/muqsit/invmenu/InvMenuHandler.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ final class InvMenuHandler{
1818
private static PlayerManager $player_manager;
1919

2020
public static function register(Plugin $plugin) : void{
21-
if(self::isRegistered()){
22-
throw new InvalidArgumentException("{$plugin->getName()} attempted to register " . self::class . " twice.");
23-
}
24-
21+
!self::isRegistered() || throw new InvalidArgumentException("{$plugin->getName()} attempted to register " . self::class . " twice.");
2522
self::$registrant = $plugin;
2623
self::$type_registry = new InvMenuTypeRegistry();
2724
self::$player_manager = new PlayerManager(self::getRegistrant());

src/muqsit/invmenu/session/PlayerManager.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,4 @@ public function get(Player $player) : PlayerSession{
5353
public function getNullable(Player $player) : ?PlayerSession{
5454
return $this->sessions[$player->getId()] ?? null;
5555
}
56-
57-
/**
58-
* @deprecated Access {@see PlayerManager::$network_handler_registry} directly
59-
* @return PlayerNetworkHandlerRegistry
60-
*/
61-
public function getNetworkHandlerRegistry() : PlayerNetworkHandlerRegistry{
62-
return $this->network_handler_registry;
63-
}
6456
}

src/muqsit/invmenu/session/PlayerSession.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,4 @@ public function finalize() : void{
2929
$this->dispatcher?->finalize();
3030
$this->dispatcher = null;
3131
}
32-
33-
public function getCurrent() : ?InvMenuInfo{
34-
return $this->current;
35-
}
36-
37-
/**
38-
* @deprecated Access {@see PlayerSession::$network} directly
39-
* @return PlayerNetwork
40-
*/
41-
public function getNetwork() : PlayerNetwork{
42-
return $this->network;
43-
}
4432
}

src/muqsit/invmenu/session/PlayerWindowDispatcher.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222

2323
final class PlayerWindowDispatcher{
2424

25-
public const int STATE_SENDING = 0;
26-
public const int STATE_FINALIZING = 1;
27-
public const int STATE_COMPLETED = 2;
25+
public const STATE_SENDING = 0;
26+
public const STATE_FINALIZING = 1;
27+
public const STATE_COMPLETED = 2;
2828

2929
private ?TaskHandler $task_handler = null;
3030
private ?Closure $container_open_callback = null;
@@ -196,4 +196,4 @@ public function then(InvMenu $menu, ?string $name, ?Closure $callback) : void{
196196
}
197197
$this->after_finalization = [$menu, $name, $callback];
198198
}
199-
}
199+
}

src/muqsit/invmenu/transaction/InvMenuTransactionResult.php

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@ public function __construct(
1616
readonly public bool $cancelled
1717
){}
1818

19-
/**
20-
* @deprecated Access {@see InvMenuTransactionResult::$cancelled} directly
21-
* @return bool
22-
*/
23-
public function isCancelled() : bool{
24-
return $this->cancelled;
25-
}
26-
2719
/**
2820
* Notify when we have escaped from the event stack trace and the
2921
* client's network stack trace.
@@ -37,12 +29,4 @@ public function then(?Closure $callback) : self{
3729
$this->post_transaction_callback = $callback;
3830
return $this;
3931
}
40-
41-
/**
42-
* @deprecated Access {@see InvMenuTransactionResult::$post_transaction_callback} directly
43-
* @return (Closure(Player) : void)|null
44-
*/
45-
public function getPostTransactionCallback() : ?Closure{
46-
return $this->post_transaction_callback;
47-
}
4832
}

0 commit comments

Comments
 (0)