mirror of
https://github.com/rockyhawk64/CommandPanels.git
synced 2025-11-18 07:14:17 +01:00
add permission observer feature
This commit is contained in:
parent
dc61d9155f
commit
cd127d229b
@ -33,7 +33,7 @@ public class ActionBuilder implements Listener {
|
||||
|
||||
if (!button.getConditions().trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(button.getConditions());
|
||||
if (!conditionNode.evaluate(player, ctx)) return null;
|
||||
if (!conditionNode.evaluate(player, panel, ctx)) return null;
|
||||
}
|
||||
|
||||
Component name = ctx.text.parseTextToComponent(player, button.getName());
|
||||
|
||||
@ -61,7 +61,7 @@ public class DialogPanelBuilder extends PanelBuilder {
|
||||
// Check conditions for which component to use
|
||||
if (!comp.getConditions().trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(comp.getConditions());
|
||||
boolean result = conditionNode.evaluate(player, ctx);
|
||||
boolean result = conditionNode.evaluate(player, panel, ctx);
|
||||
if (!result) continue;
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ public class CustomForm {
|
||||
// Evaluate conditions
|
||||
if (!comp.getConditions().trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(comp.getConditions());
|
||||
if (!conditionNode.evaluate(player, ctx)) continue;
|
||||
if (!conditionNode.evaluate(player, panel, ctx)) continue;
|
||||
}
|
||||
|
||||
// Create the component
|
||||
|
||||
@ -48,7 +48,7 @@ public class SimpleForm {
|
||||
// Check conditions for which button to use in the slot
|
||||
if (!button.getConditions().trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(button.getConditions());
|
||||
boolean result = conditionNode.evaluate(p, ctx);
|
||||
boolean result = conditionNode.evaluate(p, panel, ctx);
|
||||
if (!result) continue;
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ public class PanelFactory {
|
||||
// Check conditions for which item to use in the slot
|
||||
if (!item.conditions().trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(item.conditions());
|
||||
boolean result = conditionNode.evaluate(p, ctx);
|
||||
boolean result = conditionNode.evaluate(p, panel, ctx);
|
||||
if (!result) continue;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package me.rockyhawk.commandpanels.builder.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.session.Panel;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
@ -23,7 +24,7 @@ public class ComparisonNode implements ConditionNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx) {
|
||||
public boolean evaluate(Player player, Panel panel, Context ctx) {
|
||||
/*
|
||||
Do not parse placeholders of conditions before using this it will be handled internally
|
||||
Remove spaces from placeholders before parsing, so they can be compared with no spaces correctly
|
||||
@ -48,6 +49,7 @@ public class ComparisonNode implements ConditionNode {
|
||||
return leftValue >= rightValue;
|
||||
case "$HASPERM":
|
||||
Player p = Bukkit.getPlayer(parsedLeft);
|
||||
panel.addObservedPerm(parsedRight);
|
||||
if (p == null) return false;
|
||||
return p.hasPermission(parsedRight);
|
||||
default:
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package me.rockyhawk.commandpanels.builder.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.session.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface ConditionNode {
|
||||
boolean evaluate(Player player, Context ctx);
|
||||
boolean evaluate(Player player, Panel panel, Context ctx);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package me.rockyhawk.commandpanels.builder.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.session.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
@ -15,12 +16,12 @@ public class LogicalNode implements ConditionNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx) {
|
||||
public boolean evaluate(Player player, Panel panel, Context ctx) {
|
||||
switch (operator) {
|
||||
case "$AND":
|
||||
return conditions.stream().allMatch(cond -> cond.evaluate(player, ctx));
|
||||
return conditions.stream().allMatch(cond -> cond.evaluate(player, panel, ctx));
|
||||
case "$OR":
|
||||
return conditions.stream().anyMatch(cond -> cond.evaluate(player, ctx));
|
||||
return conditions.stream().anyMatch(cond -> cond.evaluate(player, panel, ctx));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package me.rockyhawk.commandpanels.builder.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.session.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NotNode implements ConditionNode {
|
||||
@ -11,8 +12,8 @@ public class NotNode implements ConditionNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx) {
|
||||
return !child.evaluate(player, ctx);
|
||||
public boolean evaluate(Player player, Panel panel, Context ctx) {
|
||||
return !child.evaluate(player, panel, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,17 +17,17 @@ public class ConditionTag implements RequirementTagResolver {
|
||||
|
||||
@Override
|
||||
public boolean check(Context ctx, Panel panel, Player player, String raw, String args) {
|
||||
return parseCondition(ctx, player, args);
|
||||
return parseCondition(ctx, panel, player, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Context ctx, Panel panel, Player player, String raw, String args) {
|
||||
}
|
||||
|
||||
private boolean parseCondition(Context ctx, Player player, String args) {
|
||||
private boolean parseCondition(Context ctx, Panel panel, Player player, String args) {
|
||||
if (!args.trim().isEmpty()) {
|
||||
ConditionNode conditionNode = new ConditionParser().parse(args);
|
||||
return conditionNode.evaluate(player, ctx);
|
||||
return conditionNode.evaluate(player, panel, ctx);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ public class RefreshPanelTag implements CommandTagResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules refreshes as they need to happen after
|
||||
* other actions such as permission changes
|
||||
* Schedules refreshes so that it runs in the next tick
|
||||
* other commands have a better chance of running first
|
||||
*/
|
||||
@Override
|
||||
public void handle(Context ctx, Panel panel, Player player, String raw, String command) {
|
||||
|
||||
@ -12,12 +12,15 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Panel {
|
||||
private final String name;
|
||||
private final String title;
|
||||
private final String conditions;
|
||||
|
||||
private final List<String> observedPerms; // List of permissions used in conditions for a panel
|
||||
private final String command; // Command used to open the panel
|
||||
private final List<String> aliases; // Aliases for command that opens the panel
|
||||
private final List<String> commands; // Commands that run when panel is opened
|
||||
@ -31,6 +34,7 @@ public abstract class Panel {
|
||||
this.aliases = config.getStringList("aliases");
|
||||
this.commands = config.getStringList("commands");
|
||||
this.type = config.getString("type", "inventory");
|
||||
this.observedPerms = new ArrayList<>();
|
||||
}
|
||||
|
||||
// Check run for permission checks with commands
|
||||
@ -39,7 +43,7 @@ public abstract class Panel {
|
||||
if (this.conditions.trim().isEmpty()) return true;
|
||||
try {
|
||||
ConditionNode node = new ConditionParser().parse(this.conditions);
|
||||
return node.evaluate(player, ctx);
|
||||
return node.evaluate(player, this, ctx);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
@ -48,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_time");
|
||||
Long lastOpen = p.getPersistentDataContainer().get(keyTime, PersistentDataType.LONG);
|
||||
long cooldown = ctx.fileHandler.config.getInt("cooldown-ticks") * 50L; // ticks in config, converted to millis
|
||||
if (lastOpen != null && System.currentTimeMillis() - lastOpen < cooldown) {
|
||||
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) {
|
||||
ctx.text.sendError(p, Message.COOLDOWN_ERROR);
|
||||
return false;
|
||||
}
|
||||
@ -65,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 keyTime = new NamespacedKey(ctx.plugin, "last_open_time");
|
||||
NamespacedKey keyTick = new NamespacedKey(ctx.plugin, "last_open_tick");
|
||||
PersistentDataContainer container = p.getPersistentDataContainer();
|
||||
|
||||
// Time the player last opened any panel
|
||||
container.set(keyTime, PersistentDataType.LONG, System.currentTimeMillis());
|
||||
container.set(keyTick, PersistentDataType.INTEGER, Bukkit.getCurrentTick());
|
||||
|
||||
// Move current → previous
|
||||
String current = container.get(keyCurrent, PersistentDataType.STRING);
|
||||
@ -106,4 +110,15 @@ public abstract class Panel {
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observed permissions are permissions that are found from HASPERM in panels
|
||||
* They will allow panels to auto refresh if their state ever changes
|
||||
*/
|
||||
public List<String> getObservedPerms() {
|
||||
return observedPerms;
|
||||
}
|
||||
public void addObservedPerm(String node) {
|
||||
observedPerms.add(node);
|
||||
}
|
||||
}
|
||||
@ -13,11 +13,17 @@ import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InventoryPanelUpdater {
|
||||
|
||||
private ScheduledTask checkTask;
|
||||
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
|
||||
@ -58,6 +64,7 @@ public class InventoryPanelUpdater {
|
||||
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
|
||||
for (int slot = 0; slot < inv.getSize(); slot++) {
|
||||
ItemStack item = inv.getItem(slot);
|
||||
if (item == null || item.getType().isAir()) continue;
|
||||
@ -104,6 +111,18 @@ public class InventoryPanelUpdater {
|
||||
|
||||
if (!(holder instanceof InventoryPanel) || holder != panel) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do a refresh if an observed perms state changes
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
2,
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
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;
|
||||
import me.rockyhawk.commandpanels.session.inventory.InventoryPanel;
|
||||
import me.rockyhawk.commandpanels.session.inventory.PanelItem;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
@ -51,6 +53,15 @@ public class ClickEvents implements Listener {
|
||||
e.setCancelled(true);
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getPersistentDataContainer().set(lastClickTick, PersistentDataType.INTEGER, currentTick);
|
||||
String itemId = container.get(baseIdKey, PersistentDataType.STRING);
|
||||
|
||||
// Check valid interaction types
|
||||
|
||||
Loading…
Reference in New Issue
Block a user