Skip to content

Commit ab83600

Browse files
Fire bukkit inventory events for InvUI inventory interactions
1 parent 73fad35 commit ab83600

21 files changed

+2325
-101
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[versions]
22
kotlin = "2.2.21"
3-
paper = "1.21.11-rc2-R0.1-SNAPSHOT"
3+
paper = "1.21.11-R0.1-SNAPSHOT"
44
testpaper = "1.21.10-R0.1-SNAPSHOT"
55

66
[libraries]

invui/src/main/java/xyz/xenondevs/invui/InvUI.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,26 @@
1313
import java.util.function.BiConsumer;
1414

1515
/**
16-
* Main class of InvUI, managing the plugin instance.
16+
* Main class of InvUI, managing the plugin instance and global settings.
1717
*/
1818
public final class InvUI implements Listener {
1919

2020
private static final InvUI INSTANCE = new InvUI();
21+
private static final @Nullable Boolean FIRE_BUKKIT_INVENTORY_EVENTS_OVERRIDE;
22+
23+
static {
24+
String property = System.getProperty("invui.fireBukkitInventoryEvents");
25+
if (property != null) {
26+
FIRE_BUKKIT_INVENTORY_EVENTS_OVERRIDE = Boolean.parseBoolean(property);
27+
} else {
28+
FIRE_BUKKIT_INVENTORY_EVENTS_OVERRIDE = null;
29+
}
30+
}
2131

2232
private final List<Runnable> disableHandlers = new ArrayList<>();
2333
private @Nullable Plugin plugin;
2434
private BiConsumer<? super String, ? super Throwable> exceptionHandler = (msg, e) -> getPlugin().getComponentLogger().error(msg, e);
35+
private boolean fireBukkitInventoryEvents = true;
2536

2637
private InvUI() {}
2738

@@ -107,6 +118,38 @@ public void handleException(String msg, Throwable t) {
107118
exceptionHandler.accept(msg, t);
108119
}
109120

121+
/**
122+
* Whether Bukkit's {@link org.bukkit.event.inventory.InventoryClickEvent} and
123+
* {@link org.bukkit.event.inventory.InventoryDragEvent} should be called for interactions with InvUI inventories.
124+
* <p>
125+
* By default, this is {@code true}. It can be changed using {@link #setFireBukkitInventoryEvents(boolean)} or
126+
* with the system property {@code invui.fireBukkitClickEvents}. If the system property is present,
127+
* it overrides the value set using {@link #setFireBukkitInventoryEvents(boolean)}.
128+
*
129+
* @return Whether Bukkit's inventory events should be fired for interactions with InvUI inventories.
130+
*/
131+
public boolean isFireBukkitInventoryEvents() {
132+
if (FIRE_BUKKIT_INVENTORY_EVENTS_OVERRIDE != null)
133+
return FIRE_BUKKIT_INVENTORY_EVENTS_OVERRIDE;
134+
return fireBukkitInventoryEvents;
135+
}
136+
137+
/**
138+
* Sets whether Bukkit's {@link org.bukkit.event.inventory.InventoryClickEvent} and
139+
* {@link org.bukkit.event.inventory.InventoryDragEvent} should be called for interactions with InvUI inventories.
140+
* <p>
141+
* By default, this is {@code true}. It can be changed using this method or
142+
* with the system property {@code invui.fireBukkitInventoryEvents}. If the system property is present,
143+
* it overrides the value set using this method.
144+
*
145+
* @param fireBukkitInventoryEvents Whether Bukkit's {@link org.bukkit.event.inventory.InventoryClickEvent} and
146+
* {@link org.bukkit.event.inventory.InventoryDragEvent}
147+
* should be called for interactions with InvUI inventories.
148+
*/
149+
public void setFireBukkitInventoryEvents(boolean fireBukkitInventoryEvents) {
150+
this.fireBukkitInventoryEvents = fireBukkitInventoryEvents;
151+
}
152+
110153
/**
111154
* Adds a {@link Runnable} that is executed when the plugin is disabled.
112155
*

invui/src/main/java/xyz/xenondevs/invui/gui/AbstractGui.java

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.papermc.paper.datacomponent.DataComponentTypes;
44
import org.bukkit.GameMode;
55
import org.bukkit.entity.Player;
6+
import org.bukkit.event.inventory.InventoryAction;
67
import org.bukkit.inventory.ItemStack;
78
import org.bukkit.inventory.PlayerInventory;
89
import org.jetbrains.annotations.Unmodifiable;
@@ -117,9 +118,6 @@ private void handleInvSlotElementClick(SlotElement.InventoryLink element, Click
117118
Inventory inventory = element.inventory();
118119
int slot = element.slot();
119120

120-
if (inventory.callClickEvent(slot, click))
121-
return;
122-
123121
switch (click.clickType()) {
124122
case LEFT -> handleInvLeftClick(click, inventory, slot);
125123
case RIGHT -> handleInvRightClick(click, inventory, slot);
@@ -128,7 +126,7 @@ private void handleInvSlotElementClick(SlotElement.InventoryLink element, Click
128126
case SWAP_OFFHAND -> handleInvOffHandKey(click, inventory, slot);
129127
case DROP -> handleInvDrop(false, click, inventory, slot);
130128
case CONTROL_DROP -> handleInvDrop(true, click, inventory, slot);
131-
case DOUBLE_CLICK -> handleInvDoubleClick(click);
129+
case DOUBLE_CLICK -> handleInvDoubleClick(click, inventory, slot);
132130
case MIDDLE -> handleInvMiddleClick(click, inventory, slot);
133131
}
134132
}
@@ -139,25 +137,36 @@ private void handleInvLeftClick(Click click, Inventory inventory, int slot) {
139137
ItemStack clicked = inventory.getItem(slot);
140138

141139
// nothing happens if both cursor and clicked stack are empty
142-
if (clicked == null && cursor == null)
140+
if (clicked == null && cursor == null) {
141+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
143142
return;
143+
}
144144

145145
UpdateReason updateReason = new PlayerUpdateReason.Click(player, click);
146146

147147
if (cursor == null) {
148148
// if the cursor is empty, pick the stack up
149+
if (inventory.callClickEvent(slot, click, InventoryAction.PICKUP_SOME))
150+
return;
151+
149152
int amount = -inventory.addItemAmount(updateReason, slot, -clicked.getAmount());
150153
clicked.setAmount(amount);
151154
player.setItemOnCursor(clicked);
152155
} else if (clicked != null && ItemUtils2.isBundle(cursor)) {
153156
// insert clicked item into bundle on cursor
157+
if (inventory.callClickEvent(slot, click, InventoryAction.PICKUP_SOME_INTO_BUNDLE))
158+
return;
159+
154160
int toAdd = ItemUtils2.getMaxAmountToAddToBundle(cursor, clicked);
155161
toAdd = -inventory.addItemAmount(updateReason, slot, -toAdd);
156162
clicked.setAmount(toAdd);
157163
ItemUtils2.tryMoveIntoBundle(cursor, clicked); // writes back into clicked and cursor
158164
player.setItemOnCursor(cursor);
159165
} else if (clicked != null && ItemUtils2.isBundle(clicked)) {
160166
// insert cursor item into clicked bundle
167+
if (inventory.callClickEvent(slot, click, InventoryAction.PLACE_SOME_INTO_BUNDLE))
168+
return;
169+
161170
ItemStack bundle = clicked.clone();
162171
if (ItemUtils2.tryMoveIntoBundle(bundle, cursor.clone())) { // writes back into bundle
163172
// some items were inserted, now try to place the updated bundle into the inventory
@@ -171,6 +180,9 @@ private void handleInvLeftClick(Click click, Inventory inventory, int slot) {
171180
}
172181
} else if (clicked == null || cursor.isSimilar(clicked)) {
173182
// if there are no items, or they're similar to the cursor, add the cursor items to the stack
183+
if (inventory.callClickEvent(slot, click, InventoryAction.PLACE_SOME))
184+
return;
185+
174186
int remains = inventory.putItem(updateReason, slot, cursor);
175187
if (remains == 0) {
176188
player.setItemOnCursor(null);
@@ -180,6 +192,9 @@ private void handleInvLeftClick(Click click, Inventory inventory, int slot) {
180192
}
181193
} else if (!cursor.isSimilar(clicked)) {
182194
// if the stacks are not similar, swap them
195+
if (inventory.callClickEvent(slot, click, InventoryAction.SWAP_WITH_CURSOR))
196+
return;
197+
183198
if (inventory.setItem(updateReason, slot, cursor))
184199
player.setItemOnCursor(clicked);
185200
}
@@ -191,13 +206,18 @@ private void handleInvRightClick(Click click, Inventory inventory, int slot) {
191206
ItemStack clicked = inventory.getItem(slot);
192207

193208
// nothing happens if both cursor and clicked stack are empty
194-
if (clicked == null && cursor == null)
209+
if (clicked == null && cursor == null) {
210+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
195211
return;
212+
}
196213

197214
UpdateReason updateReason = new PlayerUpdateReason.Click(player, click);
198215

199216
if (cursor == null && ItemUtils2.isBundle(clicked)) {
200217
// take the selected item from the bundle
218+
if (inventory.callClickEvent(slot, click, InventoryAction.PICKUP_FROM_BUNDLE))
219+
return;
220+
201221
ItemStack bundle = clicked.clone();
202222
ItemStack taken = ItemUtils2.takeSelectedFromBundle(bundle); // writes back to bundle
203223
if (taken != null) {
@@ -213,13 +233,19 @@ private void handleInvRightClick(Click click, Inventory inventory, int slot) {
213233
} else if (cursor == null) {
214234
// if the cursor is empty, split the stack to the cursor
215235
// if the stack is not divisible by 2, give the cursor the bigger part
236+
if (inventory.callClickEvent(slot, click, InventoryAction.PICKUP_HALF))
237+
return;
238+
216239
int newCursorAmount = (int) Math.ceil(clicked.getAmount() / 2.0);
217240
newCursorAmount = -inventory.addItemAmount(updateReason, slot, -newCursorAmount);
218241
ItemStack newCursor = clicked.clone();
219242
newCursor.setAmount(newCursorAmount);
220243
player.setItemOnCursor(newCursor);
221244
} else if (clicked == null && ItemUtils2.isBundle(cursor)) {
222245
// if the player right-clicked on an empty slot with a bundle, place the first item from the bundle there
246+
if (inventory.callClickEvent(slot, click, InventoryAction.PLACE_FROM_BUNDLE))
247+
return;
248+
223249
ItemStack toTake = ItemUtils2.getFirstFromBundle(cursor);
224250
if (toTake != null) {
225251
int amountTaken = toTake.getAmount() - inventory.putItem(updateReason, slot, toTake);
@@ -231,6 +257,9 @@ private void handleInvRightClick(Click click, Inventory inventory, int slot) {
231257
}
232258
} else if (clicked == null || cursor.isSimilar(clicked)) {
233259
// put one item from the cursor in the inventory
260+
if (inventory.callClickEvent(slot, click, InventoryAction.PLACE_ONE))
261+
return;
262+
234263
ItemStack toAdd = cursor.clone();
235264
toAdd.setAmount(1);
236265
int remains = inventory.putItem(updateReason, slot, toAdd);
@@ -240,6 +269,9 @@ private void handleInvRightClick(Click click, Inventory inventory, int slot) {
240269
}
241270
} else {
242271
// swap cursor and clicked
272+
if (inventory.callClickEvent(slot, click, InventoryAction.SWAP_WITH_CURSOR))
273+
return;
274+
243275
if (inventory.setItem(updateReason, slot, cursor))
244276
player.setItemOnCursor(clicked);
245277
}
@@ -249,7 +281,12 @@ private void handleInvItemShift(Click click, Inventory inventory, int slot) {
249281
Player player = click.player();
250282
ItemStack clicked = inventory.getItem(slot);
251283

252-
if (clicked == null)
284+
if (clicked == null) {
285+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
286+
return;
287+
}
288+
289+
if (inventory.callClickEvent(slot, click, InventoryAction.MOVE_TO_OTHER_INVENTORY))
253290
return;
254291

255292
UpdateReason updateReason = new PlayerUpdateReason.Click(player, click);
@@ -293,6 +330,14 @@ private void handleInvNumberKey(Click click, Inventory inventory, int slot) {
293330
return;
294331

295332
ItemStack hotbar = otherInventory.getItem(otherSlot);
333+
if (clicked == null && hotbar == null) {
334+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
335+
return;
336+
}
337+
338+
if (inventory.callClickEvent(slot, click, InventoryAction.HOTBAR_SWAP))
339+
return;
340+
296341
var updateReason = new PlayerUpdateReason.Click(click);
297342

298343
// check if clicked inventory would allow hotbar swap
@@ -316,6 +361,14 @@ private void handleInvOffHandKey(Click click, Inventory inventory, int slot) {
316361
ItemStack clicked = inventory.getItem(slot);
317362
ItemStack offhandItem = ItemUtils.takeUnlessEmpty(playerInventory.getItemInOffHand());
318363

364+
if (clicked == null && offhandItem == null) {
365+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
366+
return;
367+
}
368+
369+
if (inventory.callClickEvent(slot, click, InventoryAction.HOTBAR_SWAP))
370+
return;
371+
319372
if (inventory.setItem(new PlayerUpdateReason.Click(click), slot, offhandItem))
320373
playerInventory.setItemInOffHand(clicked);
321374
}
@@ -325,6 +378,9 @@ private void handleInvDrop(boolean ctrl, Click click, Inventory inventory, int s
325378
if (ItemUtils.isEmpty(clicked))
326379
return;
327380

381+
if (inventory.callClickEvent(slot, click, ctrl ? InventoryAction.DROP_ALL_SLOT : InventoryAction.DROP_ONE_SLOT))
382+
return;
383+
328384
Player player = click.player();
329385
UpdateReason updateReason = new PlayerUpdateReason.Click(player, click);
330386

@@ -344,9 +400,14 @@ private void handleInvDrop(boolean ctrl, Click click, Inventory inventory, int s
344400
inventory.callPostUpdateEvent(updateReason, slot, clicked, newItem);
345401
}
346402

347-
private void handleInvDoubleClick(Click click) {
403+
private void handleInvDoubleClick(Click click, Inventory clickedinventory, int clickedSlot) {
348404
Player player = click.player();
349-
if (ItemUtils.isEmpty(player.getItemOnCursor()))
405+
if (ItemUtils.isEmpty(player.getItemOnCursor())) {
406+
clickedinventory.callClickEvent(clickedSlot, click, InventoryAction.NOTHING);
407+
return;
408+
}
409+
410+
if (clickedinventory.callClickEvent(clickedSlot, click, InventoryAction.COLLECT_TO_CURSOR))
350411
return;
351412

352413
// requires window as collect to cursor is a cross-gui operation
@@ -374,13 +435,14 @@ private void handleInvDoubleClick(Click click) {
374435

375436
private void handleInvMiddleClick(Click click, Inventory inventory, int slot) {
376437
Player player = click.player();
377-
if (player.getGameMode() != GameMode.CREATIVE || !player.getItemOnCursor().isEmpty())
378-
return;
379-
380-
ItemStack cursor = inventory.getItem(slot);
381-
if (cursor != null) {
382-
cursor.setAmount(cursor.getMaxStackSize());
383-
player.setItemOnCursor(cursor);
438+
ItemStack target = inventory.getItem(slot);
439+
if (player.getGameMode() == GameMode.CREATIVE && target != null && ItemUtils.isEmpty(player.getItemOnCursor())) {
440+
if (inventory.callClickEvent(slot, click, InventoryAction.CLONE_STACK))
441+
return;
442+
target.setAmount(target.getMaxStackSize());
443+
player.setItemOnCursor(target);
444+
} else {
445+
inventory.callClickEvent(slot, click, InventoryAction.NOTHING);
384446
}
385447
}
386448

@@ -392,7 +454,7 @@ private void handleInvBundleSelect(Player player, Inventory inventory, int slot,
392454
inventory.setItem(new PlayerUpdateReason.BundleSelect(player, bundleSlot), slot, bundle);
393455
}
394456
}
395-
457+
396458
/**
397459
* Puts the given {@link ItemStack} into the first inventory that accepts it of the given collection of inventories.
398460
* If one inventory accepts any amount of items, further inventories will not be queried, meaning that an item stack

invui/src/main/java/xyz/xenondevs/invui/internal/menu/CustomContainerMenu.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import net.minecraft.network.protocol.common.ClientboundPingPacket;
2222
import net.minecraft.network.protocol.common.ServerboundPongPacket;
2323
import net.minecraft.network.protocol.game.*;
24-
import net.minecraft.resources.RegistryOps;
2524
import net.minecraft.resources.Identifier;
25+
import net.minecraft.resources.RegistryOps;
2626
import net.minecraft.server.MinecraftServer;
2727
import net.minecraft.server.level.ServerPlayer;
2828
import net.minecraft.util.HashOps;
@@ -35,7 +35,6 @@
3535
import org.bukkit.Bukkit;
3636
import org.bukkit.craftbukkit.entity.CraftPlayer;
3737
import org.bukkit.craftbukkit.inventory.CraftInventory;
38-
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
3938
import org.bukkit.craftbukkit.inventory.CraftItemStack;
4039
import org.bukkit.entity.Player;
4140
import org.bukkit.event.inventory.ClickType;
@@ -49,6 +48,7 @@
4948
import xyz.xenondevs.invui.internal.util.InventoryUtils;
5049
import xyz.xenondevs.invui.internal.util.MathUtils;
5150
import xyz.xenondevs.invui.internal.util.PingData;
51+
import xyz.xenondevs.invui.internal.util.FakeInventoryView;
5252
import xyz.xenondevs.invui.window.Window;
5353

5454
import java.time.Duration;
@@ -486,8 +486,8 @@ private void handleNormalClick(ServerboundContainerClickPacket packet) {
486486
case CLONE -> ClickType.MIDDLE;
487487

488488
case THROW -> switch (packet.buttonNum()) {
489-
case 0 -> ClickType.DROP;
490-
case 1 -> ClickType.CONTROL_DROP;
489+
case 0 -> packet.slotNum() > 0 ? ClickType.DROP : ClickType.LEFT;
490+
case 1 -> packet.slotNum() > 0 ? ClickType.CONTROL_DROP : ClickType.RIGHT;
491491
default -> ClickType.UNKNOWN;
492492
};
493493

@@ -542,7 +542,6 @@ private boolean handleDragClick(ServerboundContainerClickPacket packet) {
542542
getWindowEvents().handleClick(slot, new Click(player, dragMode, -1));
543543
} else {
544544
getWindowEvents().handleDrag(dragSlots, dragMode);
545-
546545
}
547546
});
548547
dragSlots.clear();
@@ -677,7 +676,7 @@ public void broadcastCarriedItem() {
677676

678677
@Override
679678
public InventoryView getBukkitView() {
680-
return new CraftInventoryView<>(player, bukkitInventory, this);
679+
return new FakeInventoryView(player, bukkitInventory);
681680
}
682681

683682
@Override

0 commit comments

Comments
 (0)