Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2ff7403
Clean up GUI session state
BenCodez Aug 23, 2026
7788ea8
Keep inventory click handling on Bukkit thread
BenCodez Aug 23, 2026
82dff7b
Consolidate updating inventory button lifecycle
BenCodez Aug 23, 2026
22b51a4
Centralize inventory pagination math
BenCodez Aug 23, 2026
fbccf80
Clean up inventory pagination and timer lifecycle
BenCodez Aug 23, 2026
8021c31
Track updating button tasks per viewer
BenCodez Aug 23, 2026
2114b15
Use shared pagination mapping in GUI listener
BenCodez Aug 23, 2026
61323ba
Test per-viewer inventory timer ownership
BenCodez Aug 23, 2026
fb9534f
Test inventory pagination boundaries
BenCodez Aug 23, 2026
262efe5
Test GUI session validation and extraction
BenCodez Aug 23, 2026
31d52df
Test updating button viewer scheduling
BenCodez Aug 23, 2026
c52095b
Test synchronous GUI click dispatch
BenCodez Aug 23, 2026
04eed7b
Fix updating button fill-slot branch
BenCodez Aug 23, 2026
387d3a7
Identify GUI clicks by top inventory
BenCodez Aug 23, 2026
090e8b2
Avoid Bukkit registry initialization in listener test
BenCodez Aug 23, 2026
eae45bd
Cancel legacy GUI update tasks on force close
BenCodez Aug 23, 2026
3ec9e34
Test legacy timer cleanup on force close
BenCodez Aug 23, 2026
08efab8
Preserve async GUI click callbacks by default
BenCodez Aug 23, 2026
d28ea64
Make GUI click callback threading configurable
BenCodez Aug 23, 2026
39653a7
Test configurable GUI click threading
BenCodez Aug 23, 2026
ec103aa
Test async default and sync GUI click modes
BenCodez Aug 23, 2026
2f78248
Fix fill-slot loop after click mode update
BenCodez Aug 23, 2026
fdc19aa
Fix permission block after click mode update
BenCodez Aug 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,182 +1,161 @@
package com.bencodez.advancedcore.api.inventory;

import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;

import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.simpleapi.messages.MessageAPI;

/**
* Listener for inventory interactions.
*/
public class BInventoryListener implements Listener {
private AdvancedCorePlugin plugin;

/**
* Instantiates a new BInventory listener.
*
* @param plugin the plugin instance
*/
public BInventoryListener(AdvancedCorePlugin plugin) {
this.plugin = plugin;
}

/**
* Handle inventory click events.
*
* @param event the inventory click event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player)) {
return;
}

final Player player = (Player) event.getWhoClicked();

plugin.getBukkitScheduler().runTaskAsynchronously(plugin, new Runnable() {

@Override
public void run() {
if (player.getInventory().firstEmpty() != -1) {
plugin.getFullInventoryHandler().check(player);
}
}
});

final GUISession session = GUISession.extractSession(player);
if (session == null) {
return;
}
final BInventory gui = session.getInventoryGUI();
event.setCancelled(true);
event.setResult(Result.DENY);
if (event.getClickedInventory() != null && event.getClickedInventory().getType() == InventoryType.CHEST) {

if (event.isShiftClick() && event.getClickedInventory() != null
&& event.getRawSlot() < event.getInventory().getSize()) {
event.setCurrentItem(new ItemStack(Material.AIR));
}
player.setItemOnCursor(new ItemStack(Material.AIR));
player.updateInventory();

if (plugin.getOptions().isCloseGUIOnShiftClick()) {
gui.forceClose(player);
}
if (gui.isCloseInv()) {
gui.closeInv(player, null);
}

// prevent spam clicking, to avoid dupe issues on large servers
long cTime = System.currentTimeMillis();
if (cTime - gui.getLastPressTime() < plugin.getOptions().getSpamClickTimeMs()) {
plugin.debug(player.getName() + " spam clicking GUI, preventing exploits");
player.updateInventory();
event.setCurrentItem(new ItemStack(Material.AIR));
gui.forceClose(player);

// spam click message
String msg = plugin.getOptions().getSpamClickMessage();
if (!msg.isEmpty()) {
player.sendMessage(MessageAPI.colorize(msg));
}

return;
}

gui.setLastPressTime(cTime);

plugin.getBukkitScheduler().runTaskAsynchronously(plugin, new Runnable() {

@Override
public void run() {
int slot = event.getSlot();
if (!gui.isPages()) {
for (int buttonSlot : gui.getButtons().keySet()) {
BInventoryButton button = gui.getButtons().get(buttonSlot);
if (slot == buttonSlot) {

gui.closeInv(player, button);

try {
gui.onClick(event, button);
} catch (Exception e) {
e.printStackTrace();
}

}

}
} else {
final int maxInvSize = gui.getMaxInvSize();
final int page = session.getPage();
final int maxPage = gui.getMaxPage();

if (slot < maxInvSize - 9) {
int buttonSlot = (page - 1) * (maxInvSize - 9) + event.getSlot();
BInventoryButton button = gui.getButtons().get(buttonSlot);
if (button != null) {
gui.closeInv(player, button);

try {
gui.onClick(event, button);
} catch (Exception e) {
e.printStackTrace();
}

return;
}

} else if (slot == maxInvSize - 9) {
if (page > 1) {

final int nextPage = page - 1;

// gui.forceClose(player);
gui.playSound(player);
gui.openInventory(player, nextPage);

}
} else if (slot == maxInvSize - 1) {
// AdvancedCorePlugin.getInstance().debug(maxPage + " " +
// page);
if (maxPage > page) {

final int nextPage = page + 1;

gui.playSound(player);
// gui.forceClose(player);
gui.openInventory(player, nextPage);

}

}

for (BInventoryButton b : gui.getPageButtons()) {
if (slot == b.getSlot() + (gui.getMaxInvSize() - 9)) {
gui.closeInv(player, b);

try {
gui.onClick(event, b);
} catch (Exception e) {
e.printStackTrace();
}
return;

}

}
}
}
});
}

}
}
package com.bencodez.advancedcore.api.inventory;

import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;

import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.simpleapi.messages.MessageAPI;
import com.bencodez.simpleapi.player.PlayerUtils;

/**
* Listener for inventory interactions.
*/
public class BInventoryListener implements Listener {
private final AdvancedCorePlugin plugin;

public BInventoryListener(AdvancedCorePlugin plugin) {
this.plugin = plugin;
}

@EventHandler(priority = EventPriority.LOWEST)
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player)) {
return;
}

Player player = (Player) event.getWhoClicked();
scheduleFullInventoryCheck(player);

GUISession session = GUISession.extractSession(player);
if (session == null) {
return;
}

BInventory gui = session.getInventoryGUI();
event.setCancelled(true);
event.setResult(Result.DENY);

if (event.getClickedInventory() == null || event.getClickedInventory() != PlayerUtils.getTopInventory(player)) {
return;
}

prepareClick(event, player, gui);
if (isSpamClick(player, gui, event)) {
return;
}

gui.setLastPressTime(System.currentTimeMillis());
if (gui.isPages()) {
handlePagedClick(event, player, gui, session);
} else {
handleButtonClick(event, player, gui, gui.getButtons().get(event.getSlot()));
}
}

private void scheduleFullInventoryCheck(Player player) {
plugin.getBukkitScheduler().runTask(plugin, () -> {
if (player.getInventory().firstEmpty() != -1) {
plugin.getFullInventoryHandler().check(player);
}
}, player);
}

private void prepareClick(InventoryClickEvent event, Player player, BInventory gui) {
if (event.isShiftClick() && event.getRawSlot() < event.getInventory().getSize()) {
event.setCurrentItem(new ItemStack(Material.AIR));
}
player.setItemOnCursor(new ItemStack(Material.AIR));
player.updateInventory();

if (plugin.getOptions().isCloseGUIOnShiftClick()) {
gui.forceClose(player);
}
if (gui.isCloseInv()) {
gui.closeInv(player, null);
}
}

private boolean isSpamClick(Player player, BInventory gui, InventoryClickEvent event) {
long currentTime = System.currentTimeMillis();
if (currentTime - gui.getLastPressTime() >= plugin.getOptions().getSpamClickTimeMs()) {
return false;
}

plugin.debug(player.getName() + " spam clicking GUI, preventing exploits");
player.updateInventory();
event.setCurrentItem(new ItemStack(Material.AIR));
gui.forceClose(player);

String message = plugin.getOptions().getSpamClickMessage();
if (!message.isEmpty()) {
player.sendMessage(MessageAPI.colorize(message));
}
return true;
}

private void handlePagedClick(InventoryClickEvent event, Player player, BInventory gui, GUISession session) {
int slot = event.getSlot();
int maxInventorySize = gui.getMaxInvSize();
int contentSize = InventoryPagination.getContentSize(maxInventorySize);
int page = session.getPage();

if (InventoryPagination.isContentSlot(slot, maxInventorySize)) {
int buttonSlot = InventoryPagination.getButtonSlot(page, slot, maxInventorySize);
BInventoryButton button = gui.getButtons().get(buttonSlot);
if (button != null) {
handleButtonClick(event, player, gui, button);
}
return;
}

if (slot == contentSize) {
if (page > 1) {
gui.playSound(player);
gui.openInventory(player, page - 1);
}
return;
}

if (slot == maxInventorySize - 1) {
if (page < gui.getMaxPage()) {
gui.playSound(player);
gui.openInventory(player, page + 1);
}
return;
}

for (BInventoryButton button : gui.getPageButtons()) {
if (slot == button.getSlot() + contentSize) {
handleButtonClick(event, player, gui, button);
return;
}
}
}

private void handleButtonClick(InventoryClickEvent event, Player player, BInventory gui, BInventoryButton button) {
if (button == null) {
return;
}

gui.closeInv(player, button);
Runnable clickAction = () -> {
try {
gui.onClick(event, button);
} catch (Exception exception) {
plugin.debug(exception);
}
};

if (gui.isClickAsync()) {
plugin.getBukkitScheduler().runTaskAsynchronously(plugin, clickAction);
} else {
clickAction.run();
}
}
}
Loading
Loading