mirror of
https://github.com/rockyhawk64/CommandPanels.git
synced 2025-11-18 07:14:17 +01:00
Compare commits
4 Commits
4b1a7c87f8
...
732995f2f2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
732995f2f2 | ||
|
|
7d27cc133b | ||
|
|
334856a421 | ||
|
|
8b80ab0ed3 |
@ -1,5 +1,5 @@
|
||||
name: CommandPanels
|
||||
version: 4.1.0
|
||||
version: 4.1.1
|
||||
api-version: 1.21.10
|
||||
|
||||
main: me.rockyhawk.commandpanels.CommandPanels
|
||||
|
||||
@ -70,7 +70,10 @@ public class CommandRunner {
|
||||
}
|
||||
|
||||
// Run the command
|
||||
runCommand(panel, player, command);
|
||||
Bukkit.getGlobalRegionScheduler().run(
|
||||
ctx.plugin,
|
||||
task -> runCommand(panel, player, command)
|
||||
);
|
||||
|
||||
// Move to the next command
|
||||
runCommands(panel, player, commands, index + 1);
|
||||
|
||||
@ -47,7 +47,7 @@ public class TeleportTag implements CommandTagResolver {
|
||||
}
|
||||
Location teleportLocation = new Location(teleportedWorld, x, y, z, yaw, pitch);
|
||||
if (teleportedPlayer != null) {
|
||||
teleportedPlayer.teleport(teleportLocation);
|
||||
player.teleportAsync(teleportLocation);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ctx.text.sendError(player, Message.TELEPORT_ERROR);
|
||||
|
||||
@ -52,10 +52,10 @@ public abstract class Panel {
|
||||
// Checks for opening fresh panels
|
||||
public boolean canOpen(Player p, Context ctx) {
|
||||
// Do not open if user is in cooldown period
|
||||
NamespacedKey keyTime = new NamespacedKey(ctx.plugin, "last_open_tick");
|
||||
Integer lastOpenTick = p.getPersistentDataContainer().get(keyTime, PersistentDataType.INTEGER);
|
||||
int cooldownTicks = ctx.fileHandler.config.getInt("cooldown-ticks");
|
||||
if (lastOpenTick != null && Bukkit.getCurrentTick() - lastOpenTick < cooldownTicks) {
|
||||
NamespacedKey keyTime = new NamespacedKey(ctx.plugin, "last_open_time");
|
||||
Long lastOpenTime = p.getPersistentDataContainer().get(keyTime, PersistentDataType.LONG);
|
||||
long cooldownMillis = ctx.fileHandler.config.getLong("cooldown-ticks") * 50L;
|
||||
if (lastOpenTime != null && System.currentTimeMillis() - lastOpenTime < cooldownMillis) {
|
||||
ctx.text.sendError(p, Message.COOLDOWN_ERROR);
|
||||
return false;
|
||||
}
|
||||
@ -69,11 +69,11 @@ public abstract class Panel {
|
||||
public void updatePanelData(Context ctx, Player p) {
|
||||
NamespacedKey keyCurrent = new NamespacedKey(ctx.plugin, "current");
|
||||
NamespacedKey keyPrevious = new NamespacedKey(ctx.plugin, "previous");
|
||||
NamespacedKey keyTick = new NamespacedKey(ctx.plugin, "last_open_tick");
|
||||
NamespacedKey keyMillis = new NamespacedKey(ctx.plugin, "last_open_time");
|
||||
PersistentDataContainer container = p.getPersistentDataContainer();
|
||||
|
||||
// Time the player last opened any panel
|
||||
container.set(keyTick, PersistentDataType.INTEGER, Bukkit.getCurrentTick());
|
||||
container.set(keyMillis, PersistentDataType.LONG, System.currentTimeMillis());
|
||||
|
||||
// Move current → previous
|
||||
String current = container.get(keyCurrent, PersistentDataType.STRING);
|
||||
|
||||
@ -15,14 +15,20 @@ public class SessionDataUtils implements Listener {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* On player Join and Leave,
|
||||
* Remove Session data and do an async save of the data file
|
||||
*/
|
||||
@EventHandler
|
||||
public void onJoinEvent(PlayerJoinEvent e) {
|
||||
removeSessionData(e.getPlayer());
|
||||
ctx.dataLoader.saveDataFileAsync();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuitEvent(PlayerQuitEvent e) {
|
||||
removeSessionData(e.getPlayer());
|
||||
ctx.dataLoader.saveDataFileAsync();
|
||||
}
|
||||
|
||||
private void removeSessionData(Player p){
|
||||
|
||||
@ -4,6 +4,7 @@ import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.builder.PanelBuilder;
|
||||
import me.rockyhawk.commandpanels.builder.inventory.InventoryPanelBuilder;
|
||||
import me.rockyhawk.commandpanels.interaction.commands.CommandRunner;
|
||||
import me.rockyhawk.commandpanels.session.ClickActions;
|
||||
import me.rockyhawk.commandpanels.session.Panel;
|
||||
import me.rockyhawk.commandpanels.session.floodgate.FloodgatePanel;
|
||||
import org.bukkit.Bukkit;
|
||||
@ -22,6 +23,7 @@ public class InventoryPanel extends Panel implements InventoryHolder {
|
||||
private final String rows;
|
||||
private final Map<String, PanelItem> items = new HashMap<>();
|
||||
private final Map<String, List<String>> slots = new HashMap<>();
|
||||
private final ClickActions outside;
|
||||
private final String floodgate;
|
||||
private final String updateDelay;
|
||||
|
||||
@ -32,6 +34,12 @@ public class InventoryPanel extends Panel implements InventoryHolder {
|
||||
this.floodgate = config.getString("floodgate", "");
|
||||
this.updateDelay = config.getString("update-delay", "20");
|
||||
|
||||
outside = new ClickActions(
|
||||
config.getStringList("outside.requirements"),
|
||||
config.getStringList("outside.commands"),
|
||||
config.getStringList("outside.fail")
|
||||
);
|
||||
|
||||
ConfigurationSection slotSection = config.getConfigurationSection("layout");
|
||||
if (slotSection != null) {
|
||||
for (String key : slotSection.getKeys(false)) {
|
||||
@ -104,9 +112,14 @@ public class InventoryPanel extends Panel implements InventoryHolder {
|
||||
return updateDelay;
|
||||
}
|
||||
|
||||
public ClickActions getOutsideCommands() {
|
||||
return outside;
|
||||
}
|
||||
|
||||
// For InventoryHolder implementation
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,52 +17,72 @@ import java.util.Map;
|
||||
|
||||
public class InventoryPanelUpdater {
|
||||
|
||||
private ScheduledTask checkTask;
|
||||
private ScheduledTask heartbeatTask;
|
||||
private ScheduledTask updateTask;
|
||||
|
||||
// List of permission states for observed permissions
|
||||
private final Map<String, Boolean> lastObservedPermStates = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Panel updater will maintain itself with a checkTask that will end the updater
|
||||
* If it finds the panel has been closed it will end the updater tasks
|
||||
*/
|
||||
|
||||
public void start(Context ctx, Player p, InventoryPanel panel) {
|
||||
// Stop existing tasks if any
|
||||
stop();
|
||||
stop(); // always clean slate
|
||||
|
||||
// Determine update delay
|
||||
int updateDelay = 20;
|
||||
if (panel.getUpdateDelay().matches("\\d+")) {
|
||||
updateDelay = Integer.parseInt(panel.getUpdateDelay());
|
||||
startHeartbeat(ctx, p, panel);
|
||||
|
||||
int updateDelay = parseUpdateDelay(panel.getUpdateDelay());
|
||||
if (updateDelay > 0) {
|
||||
startUpdater(ctx, p, panel, updateDelay);
|
||||
}
|
||||
}
|
||||
|
||||
// If update delay is 0 then do not run the updater
|
||||
if (updateDelay == 0) {
|
||||
this.updateTask = null;
|
||||
return;
|
||||
}
|
||||
private void startHeartbeat(Context ctx, Player p, InventoryPanel panel) {
|
||||
final boolean isUsingPermObserver = ctx.fileHandler.config.getBoolean("permission-observer");
|
||||
|
||||
InventoryPanelBuilder panelBuilder = new InventoryPanelBuilder(ctx, p);
|
||||
ItemBuilder builder = new ItemBuilder(ctx, panelBuilder);
|
||||
|
||||
// Main update task
|
||||
this.updateTask = p.getScheduler().runAtFixedRate(
|
||||
heartbeatTask = p.getScheduler().runAtFixedRate(
|
||||
ctx.plugin,
|
||||
(scheduledTask) -> {
|
||||
(task) -> {
|
||||
Inventory inv = p.getOpenInventory().getTopInventory();
|
||||
InventoryHolder holder = inv.getHolder();
|
||||
|
||||
// Stop everything if the panel is closed
|
||||
if (!(holder instanceof InventoryPanel) || holder != panel) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle permission observer
|
||||
if (!isUsingPermObserver) return;
|
||||
for (String node : panel.getObservedPerms()) {
|
||||
boolean current = p.hasPermission(node);
|
||||
Boolean previous = lastObservedPermStates.put(node, current);
|
||||
if (previous != null && previous != current) {
|
||||
panel.open(ctx, p, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
2,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
private void startUpdater(Context ctx, Player p, InventoryPanel panel, int updateDelay) {
|
||||
InventoryPanelBuilder panelBuilder = new InventoryPanelBuilder(ctx, p);
|
||||
ItemBuilder builder = new ItemBuilder(ctx, panelBuilder);
|
||||
|
||||
NamespacedKey itemIdKey = new NamespacedKey(ctx.plugin, "item_id");
|
||||
NamespacedKey baseIdKey = new NamespacedKey(ctx.plugin, "base_item_id");
|
||||
NamespacedKey fillItem = new NamespacedKey(ctx.plugin, "fill_item");
|
||||
|
||||
// Loop through items in the panel and update their state
|
||||
updateTask = p.getScheduler().runAtFixedRate(
|
||||
ctx.plugin,
|
||||
(task) -> {
|
||||
Inventory inv = p.getOpenInventory().getTopInventory();
|
||||
InventoryHolder holder = inv.getHolder();
|
||||
if (!(holder instanceof InventoryPanel) || holder != panel) {
|
||||
stopUpdater(); // only stop this task, heartbeat may continue
|
||||
return;
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < inv.getSize(); slot++) {
|
||||
ItemStack item = inv.getItem(slot);
|
||||
if (item == null || item.getType().isAir()) continue;
|
||||
@ -99,44 +119,28 @@ public class InventoryPanelUpdater {
|
||||
updateDelay,
|
||||
updateDelay
|
||||
);
|
||||
|
||||
final boolean isUsingPermObserver = ctx.fileHandler.config.getBoolean("permission-observer");
|
||||
|
||||
// Fast heartbeat check task, should run frequently
|
||||
this.checkTask = p.getScheduler().runAtFixedRate(
|
||||
ctx.plugin,
|
||||
(scheduledTask) -> {
|
||||
Inventory inv = p.getOpenInventory().getTopInventory();
|
||||
InventoryHolder holder = inv.getHolder();
|
||||
|
||||
if (!(holder instanceof InventoryPanel) || holder != panel) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Permission Observer: Refresh if an observed perms state changes
|
||||
if(!isUsingPermObserver) return; // Skip if disabled
|
||||
for (String node : panel.getObservedPerms()) {
|
||||
boolean currentState = p.hasPermission(node);
|
||||
Boolean previousState = lastObservedPermStates.get(node);
|
||||
lastObservedPermStates.put(node, currentState);
|
||||
if (previousState != null && previousState != currentState) {
|
||||
panel.open(ctx, p, false);
|
||||
return;
|
||||
private int parseUpdateDelay(String delayStr) {
|
||||
if (delayStr != null && delayStr.matches("\\d+")) {
|
||||
return Integer.parseInt(delayStr);
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
2,
|
||||
2
|
||||
);
|
||||
return 20; // default
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (checkTask != null) {
|
||||
checkTask.cancel();
|
||||
checkTask = null;
|
||||
stopHeartbeat();
|
||||
stopUpdater();
|
||||
}
|
||||
|
||||
private void stopHeartbeat() {
|
||||
if (heartbeatTask != null) {
|
||||
heartbeatTask.cancel();
|
||||
heartbeatTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopUpdater() {
|
||||
if (updateTask != null) {
|
||||
updateTask.cancel();
|
||||
updateTask = null;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package me.rockyhawk.commandpanels.session.inventory.listeners;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.formatter.language.Message;
|
||||
import me.rockyhawk.commandpanels.interaction.commands.CommandRunner;
|
||||
import me.rockyhawk.commandpanels.interaction.commands.RequirementRunner;
|
||||
import me.rockyhawk.commandpanels.session.ClickActions;
|
||||
@ -33,6 +32,20 @@ public class ClickEvents implements Listener {
|
||||
requirements = new RequirementRunner(ctx);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onOutsideInventoryClick(InventoryClickEvent e) {
|
||||
if (!(e.getWhoClicked() instanceof Player player)) return;
|
||||
if (e.getClickedInventory() != null) return;
|
||||
if (!(player.getOpenInventory().getTopInventory().getHolder() instanceof InventoryPanel panel)) return;
|
||||
|
||||
ClickActions actions = panel.getOutsideCommands();
|
||||
if(!requirements.processRequirements(panel, player, actions.requirements())){
|
||||
commands.runCommands(panel, player, actions.fail());
|
||||
return;
|
||||
}
|
||||
commands.runCommands(panel, player, actions.commands());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent e) {
|
||||
if (!(e.getWhoClicked() instanceof Player player)) return;
|
||||
@ -54,14 +67,14 @@ public class ClickEvents implements Listener {
|
||||
e.setResult(Event.Result.DENY);
|
||||
|
||||
// Do not run commands if user is in cooldown (item click cooldown should match heartbeat updater speed)
|
||||
NamespacedKey lastClickTick = new NamespacedKey(ctx.plugin, "last_click_tick");
|
||||
Integer lastOpen = player.getPersistentDataContainer().get(lastClickTick, PersistentDataType.INTEGER);
|
||||
int currentTick = Bukkit.getCurrentTick();
|
||||
if (lastOpen != null && currentTick - lastOpen < 2) {
|
||||
NamespacedKey lastClick = new NamespacedKey(ctx.plugin, "last_click_time");
|
||||
Long lastOpenMillis = player.getPersistentDataContainer().get(lastClick, PersistentDataType.LONG);
|
||||
long currentMillis = System.currentTimeMillis();
|
||||
if (lastOpenMillis != null && currentMillis - lastOpenMillis < 100L) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getPersistentDataContainer().set(lastClickTick, PersistentDataType.INTEGER, currentTick);
|
||||
player.getPersistentDataContainer().set(lastClick, PersistentDataType.LONG, currentMillis);
|
||||
String itemId = container.get(baseIdKey, PersistentDataType.STRING);
|
||||
|
||||
// Check valid interaction types
|
||||
|
||||
Loading…
Reference in New Issue
Block a user