mirror of
https://github.com/rockyhawk64/CommandPanels.git
synced 2025-11-18 07:14:17 +01:00
3.23.2
This commit is contained in:
parent
b1e8b8194e
commit
6e95811583
@ -86,6 +86,11 @@
|
||||
<option name="name" value="spigot-repo" />
|
||||
<option name="url" value="https://hub.spigotmc.org/nexus/content/repositories/snapshots/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="skript-releases" />
|
||||
<option name="name" value="Skript Repository" />
|
||||
<option name="url" value="https://repo.skriptlang.org/releases" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jeff-media-public" />
|
||||
<option name="name" value="jeff-media-public" />
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
|
||||
12
README.md
12
README.md
@ -1,6 +1,6 @@
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://commandpanels.net/wiki">Wiki</a> |
|
||||
@ -25,9 +25,15 @@ Making a plugin? You can use Command Panels as a library to make your own GUIs f
|
||||
|
||||
**Floodgate Forms** to create custom Bedrock GUIs with full support for both SimpleForms and CustomForms that allows inputs such as sliders, dropdowns, etc.
|
||||
|
||||
## Server Compatibility
|
||||
|
||||
**Spigot & Paper** Full compatibility with Spigot servers and Paper servers, with enhanced performance on Paper. 1.8-1.21.5(Latest usually works)
|
||||
|
||||
**Folia Support** Native support for Folia's regionalized threading system. The plugin automatically detects Folia and uses region-specific schedulers for optimal performance without any configuration needed.
|
||||
|
||||
## Partner
|
||||
|
||||

|
||||

|
||||
|
||||
We have proudly partnered with [ReviveNode](http://billing.revivenode.com/aff.php?aff=379)!
|
||||
CommandPanels users have been offered 15% off on the first month by using the Promocode: **PANELS**
|
||||
|
||||
17
pom.xml
17
pom.xml
@ -123,13 +123,20 @@
|
||||
<id>opencollab-snapshot</id>
|
||||
<url>https://repo.opencollab.dev/main/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Skript Repository -->
|
||||
<repository>
|
||||
<id>skript-releases</id>
|
||||
<name>Skript Repository</name>
|
||||
<url>https://repo.skriptlang.org/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.14.2-SNAPSHOT</version>
|
||||
<version>2.15.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -238,5 +245,13 @@
|
||||
<version>2.2.2-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Skript -->
|
||||
<dependency>
|
||||
<groupId>com.github.SkriptLang</groupId>
|
||||
<artifactId>Skript</artifactId>
|
||||
<version>2.8.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1,10 +1,10 @@
|
||||
version: 3.22.5
|
||||
version: 3.23.2
|
||||
main: me.rockyhawk.commandpanels.CommandPanels
|
||||
name: CommandPanels
|
||||
author: RockyHawk
|
||||
api-version: '1.13'
|
||||
description: Fully Custom GUIs. Make your Server Professional.
|
||||
softdepend: [Essentials, PlaceholderAPI, Vault, HeadDatabase, TokenManager, VotingPlugin, MMOItems, ChestSort, floodgate]
|
||||
softdepend: [Essentials, PlaceholderAPI, Vault, HeadDatabase, TokenManager, VotingPlugin, MMOItems, ChestSort, floodgate, Skript]
|
||||
commands:
|
||||
commandpanel:
|
||||
description: Open a command panel.
|
||||
|
||||
@ -19,32 +19,59 @@ public class CommandPanels extends JavaPlugin{
|
||||
public void onEnable() {
|
||||
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loading...");
|
||||
|
||||
//Initialise plugin context
|
||||
ctx = new Context(this);
|
||||
try {
|
||||
//Initialise plugin context
|
||||
ctx = new Context(this);
|
||||
|
||||
//add custom charts bStats
|
||||
Metrics metrics = new Metrics(this, 5097);
|
||||
metrics.addCustomChart(new SingleLineChart("panels_amount", () -> {
|
||||
//this is the total panels loaded
|
||||
return panelList.size();
|
||||
}));
|
||||
//add custom charts bStats
|
||||
Metrics metrics = new Metrics(this, 5097);
|
||||
metrics.addCustomChart(new SingleLineChart("panels_amount", () -> {
|
||||
//this is the total panels loaded
|
||||
return panelList.size();
|
||||
}));
|
||||
|
||||
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loaded!");
|
||||
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loaded!");
|
||||
} catch (Exception e) {
|
||||
Bukkit.getLogger().severe("[CommandPanels] Failed to load plugin: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
// Set ctx to null to prevent issues in onDisable
|
||||
ctx = null;
|
||||
throw e; // Re-throw to properly disable the plugin
|
||||
}
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
//close all the panels
|
||||
for(String name : ctx.openPanels.openPanels.keySet()){
|
||||
ctx.openPanels.closePanelForLoader(name, PanelPosition.Top);
|
||||
try {
|
||||
Bukkit.getPlayer(name).closeInventory();
|
||||
}catch (Exception ignore){}
|
||||
// Check if context was properly initialized before attempting cleanup
|
||||
if (ctx == null) {
|
||||
Bukkit.getLogger().info("RockyHawk's CommandPanels Plugin Disabled (initialization failed).");
|
||||
return;
|
||||
}
|
||||
|
||||
//save files
|
||||
ctx.panelData.saveDataFile();
|
||||
ctx.inventorySaver.saveInventoryFile();
|
||||
ctx.updater.autoUpdatePlugin(this.getFile().getName());
|
||||
try {
|
||||
//close all the panels
|
||||
if (ctx.openPanels != null && ctx.openPanels.openPanels != null) {
|
||||
for(String name : ctx.openPanels.openPanels.keySet()){
|
||||
ctx.openPanels.closePanelForLoader(name, PanelPosition.Top);
|
||||
try {
|
||||
Bukkit.getPlayer(name).closeInventory();
|
||||
}catch (Exception ignore){}
|
||||
}
|
||||
}
|
||||
|
||||
//save files
|
||||
if (ctx.panelData != null) {
|
||||
ctx.panelData.saveDataFile();
|
||||
}
|
||||
if (ctx.inventorySaver != null) {
|
||||
ctx.inventorySaver.saveInventoryFile();
|
||||
}
|
||||
if (ctx.updater != null) {
|
||||
ctx.updater.autoUpdatePlugin(this.getFile().getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Bukkit.getLogger().severe("Error during CommandPanels shutdown: " + e.getMessage());
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("RockyHawk's CommandPanels Plugin Disabled, aww man.");
|
||||
}
|
||||
|
||||
|
||||
@ -148,6 +148,7 @@ public class Context {
|
||||
//setup class files
|
||||
setupEconomy();
|
||||
Bukkit.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "BungeeCord");
|
||||
Bukkit.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "velocity:main");
|
||||
plugin.getCommand("commandpanel").setExecutor(new PanelCommand(this));
|
||||
plugin.getCommand("commandpanel").setTabCompleter(new PanelTabComplete(this));
|
||||
|
||||
|
||||
@ -19,6 +19,11 @@ public class PanelCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
//Check for blanket permission
|
||||
if(!sender.hasPermission("commandpanel.panel.default")){
|
||||
sender.sendMessage(ctx.text.colour(ctx.tag + ctx.configHandler.config.getString("config.format.perms")));
|
||||
return true;
|
||||
}
|
||||
//below is going to go through the files and find the right one
|
||||
Panel panel = null;
|
||||
if (args.length != 0) { //check to make sure the person hasn't just left it empty
|
||||
|
||||
@ -21,6 +21,7 @@ public class PanelTabComplete implements TabCompleter {
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if(!sender.hasPermission("commandpanel.panel.default")) return null;
|
||||
if (sender instanceof Player && args.length >= 1)
|
||||
if (label.equalsIgnoreCase("cp") || label.equalsIgnoreCase("cpanel") || label.equalsIgnoreCase("commandpanel")) {
|
||||
Player p = ((Player) sender).getPlayer();
|
||||
|
||||
@ -7,6 +7,8 @@ import me.rockyhawk.commandpanels.manager.session.PanelPosition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Placeholders {
|
||||
private final Context ctx;
|
||||
@ -49,18 +51,35 @@ public class Placeholders {
|
||||
// Parse placeholders in a string with this
|
||||
public String setPlaceholders(Panel panel, PanelPosition position, org.bukkit.entity.Player p, String str, boolean primary) {
|
||||
String[] HOLDERS = getPlaceholderEnds(panel, primary);
|
||||
while (str.contains(HOLDERS[0] + "cp-")) {
|
||||
try {
|
||||
int start = str.indexOf(HOLDERS[0] + "cp-");
|
||||
int end = str.indexOf(HOLDERS[1], start + 1);
|
||||
String identifier = str.substring(start, end).replace(HOLDERS[0] + "cp-", "").replace(HOLDERS[1], "");
|
||||
String value = resolvePlaceholder(panel, position, p, identifier);
|
||||
str = str.replace(str.substring(start, end) + HOLDERS[1], value);
|
||||
} catch (Exception ex) {
|
||||
ctx.debug.send(ex, p, ctx);
|
||||
break;
|
||||
if (HOLDERS[0] == null || HOLDERS[1] == null) return str;
|
||||
|
||||
// Escape the start/end symbols for regex
|
||||
String start = Pattern.quote(HOLDERS[0]);
|
||||
String end = Pattern.quote(HOLDERS[1]);
|
||||
Pattern pattern = Pattern.compile(start + "cp-([a-zA-Z0-9_\\-]+)" + end);
|
||||
|
||||
int maxPasses = 255;
|
||||
int count = 0;
|
||||
|
||||
String previous;
|
||||
do {
|
||||
previous = str;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
|
||||
while (matcher.find()) {
|
||||
String identifier = matcher.group(1);
|
||||
String replacement = resolvePlaceholder(panel, position, p, identifier);
|
||||
if (replacement == null) replacement = "";
|
||||
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
}
|
||||
|
||||
matcher.appendTail(sb);
|
||||
str = sb.toString();
|
||||
count++;
|
||||
|
||||
} while (!str.equals(previous) && count < maxPasses);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@ -104,6 +104,7 @@ public class CommandRunner {
|
||||
tags.add(new RefreshTag());
|
||||
tags.add(new SetCustomDataTag());
|
||||
tags.add(new SetItemTag());
|
||||
tags.add(new SkriptTag());
|
||||
tags.add(new SoundTag());
|
||||
tags.add(new TeleportTag());
|
||||
tags.add(new TitleTag());
|
||||
|
||||
@ -9,37 +9,56 @@ import me.rockyhawk.commandpanels.manager.session.PanelPosition;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BungeeTag implements TagResolver {
|
||||
|
||||
@Override
|
||||
public boolean handle(Context ctx, Panel panel, PanelPosition pos, Player player, String command) {
|
||||
String[] args = ctx.text.attachPlaceholders(panel, pos, player, command).split("\\s+"); // Arguments are space-separated
|
||||
if(command.startsWith("force-server=")){
|
||||
//this contacts bungee and tells it to send the server change command without checking permissions
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(args[1]);
|
||||
player.sendPluginMessage(ctx.plugin, "BungeeCord", out.toByteArray());
|
||||
return true;
|
||||
} else if(command.startsWith("server=")){
|
||||
if(args.length >= 3){
|
||||
//This uses custom permission: server= servername permission
|
||||
if(player.hasPermission(args[2])){
|
||||
//this contacts bungee and tells it to send the server change command whilst checking for CUSTOM permissions
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(args[1]);
|
||||
player.sendPluginMessage(ctx.plugin, "BungeeCord", out.toByteArray());
|
||||
}
|
||||
}else if (player.hasPermission("bungeecord.command.server." + args[1].toLowerCase())) {
|
||||
//this contacts bungee and tells it to send the server change command whilst checking for permissions
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(args[1]);
|
||||
player.sendPluginMessage(ctx.plugin, "BungeeCord", out.toByteArray());
|
||||
}else{
|
||||
player.sendMessage(ctx.text.colour(ctx.tag + ctx.configHandler.config.getString("config.format.perms")));
|
||||
}
|
||||
// Only handle server= and force-server= commands
|
||||
if (!command.startsWith("server=") && !command.startsWith("force-server=")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] args = ctx.text.attachPlaceholders(panel, pos, player, command).split("\\s+");
|
||||
|
||||
if (args.length <= 1) {
|
||||
player.sendMessage(ctx.text.colour(ctx.tag + "No server was given."));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Defaults
|
||||
String serverName = args[1];
|
||||
String proxyType = "bungeecord";
|
||||
String permission = "bungeecord.command.server." + serverName.toLowerCase();
|
||||
|
||||
// Parse optional args
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("perm:")) {
|
||||
permission = arg.substring("perm:".length()).toLowerCase();
|
||||
} else if (arg.startsWith("type:")) {
|
||||
String type = arg.substring("type:".length()).toLowerCase();
|
||||
if (type.equals("velocity") || type.equals("bungeecord")) {
|
||||
proxyType = type;
|
||||
} else {
|
||||
proxyType = "bungeecord"; // fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean force = command.startsWith("force-server=");
|
||||
boolean hasPermission = player.hasPermission(permission);
|
||||
|
||||
if (!force && !hasPermission) {
|
||||
player.sendMessage(ctx.text.colour(ctx.tag + ctx.configHandler.config.getString("config.format.perms")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Send plugin message
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(serverName);
|
||||
|
||||
String channel = proxyType.equals("velocity") ? "velocity:main" : "BungeeCord";
|
||||
player.sendPluginMessage(ctx.plugin, channel, out.toByteArray());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package me.rockyhawk.commandpanels.interaction.commands.tags;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import me.rockyhawk.commandpanels.interaction.commands.TagResolver;
|
||||
import me.rockyhawk.commandpanels.manager.session.PanelPosition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class NaturalPanelTag implements TagResolver {
|
||||
@Override
|
||||
public boolean handle(Context ctx, Panel panel, PanelPosition pos, Player player, String command) {
|
||||
if (command.startsWith("natural=")) {
|
||||
if (ctx.version.isBelow("1.21.6")) return true;
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("NaturalPanels");
|
||||
if (plugin != null && plugin.isEnabled()) {
|
||||
try {
|
||||
player.closeInventory();
|
||||
Class<?> clazz = Class.forName("me.rockyhawk.naturalpanels.NaturalPanels");
|
||||
Method method = clazz.getMethod("openPanel", Player.class, String.class);
|
||||
boolean result = (boolean) method.invoke(null, player, command.substring(9)); //substring "natural= "
|
||||
|
||||
if (!result) {
|
||||
ctx.text.sendMessage(player, ChatColor.RED + "Panel does not exist.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ctx.debug.send(e, player, ctx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package me.rockyhawk.commandpanels.interaction.commands.tags;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import me.rockyhawk.commandpanels.interaction.commands.TagResolver;
|
||||
import me.rockyhawk.commandpanels.manager.session.PanelPosition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SkriptTag implements TagResolver {
|
||||
|
||||
@Override
|
||||
public boolean handle(Context ctx, Panel panel, PanelPosition pos, Player player, String command) {
|
||||
if (!command.startsWith("skript=")) return false;
|
||||
|
||||
String[] args = ctx.text.attachPlaceholders(panel, pos, player, command).split("\\s+");
|
||||
args = Arrays.copyOfRange(args, 1, args.length); // Remove first element from args
|
||||
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ctx.tag + ctx.text.colour(ctx.configHandler.config.getString("config.format.error") + " skript=: No Skript command provided!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if Skript plugin is enabled
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("Skript")) {
|
||||
player.sendMessage(ctx.tag + ctx.text.colour(ctx.configHandler.config.getString("config.format.error") + " skript=: Skript plugin is not loaded!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
String commandName = args[0];
|
||||
String[] commandArgs = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0];
|
||||
|
||||
executeSkriptCommand(player, commandName, commandArgs);
|
||||
|
||||
} catch (Exception ex) {
|
||||
player.sendMessage(ctx.tag + ctx.text.colour(ctx.configHandler.config.getString("config.format.error") + " skript=: Error executing Skript command!"));
|
||||
ctx.debug.send(ex, player, ctx);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method to execute a Skript command by name
|
||||
private void executeSkriptCommand(Player player, String commandName, String[] args) {
|
||||
try {
|
||||
// Try to get the Skript command classes
|
||||
Class<?> commandsClass = Class.forName("ch.njol.skript.command.Commands");
|
||||
Class<?> scriptCommandClass = Class.forName("ch.njol.skript.command.ScriptCommand");
|
||||
|
||||
// Get the getCommands method
|
||||
Method getCommandsMethod = commandsClass.getMethod("getCommands");
|
||||
Object commands = getCommandsMethod.invoke(null);
|
||||
|
||||
// Check if the returned object is iterable
|
||||
if (commands instanceof Iterable) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterable<Object> commandList = (Iterable<Object>) commands;
|
||||
|
||||
// Find the command in Skript's registered commands
|
||||
for (Object command : commandList) {
|
||||
if (command.getClass().equals(scriptCommandClass)) {
|
||||
Method getNameMethod = command.getClass().getMethod("getName");
|
||||
String scriptCommandName = (String) getNameMethod.invoke(command);
|
||||
|
||||
if (scriptCommandName.equalsIgnoreCase(commandName)) {
|
||||
// Execute the command for the player with given arguments
|
||||
Method executeMethod = command.getClass().getMethod("execute", Player.class, String.class, String[].class);
|
||||
executeMethod.invoke(command, player, commandName, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage("Command '" + commandName + "' not found in Skript!");
|
||||
|
||||
} catch (Exception e) {
|
||||
// If the direct approach fails, try alternative method using functions
|
||||
try {
|
||||
executeSkriptFunction(commandName, new Object[]{player});
|
||||
} catch (Exception ex) {
|
||||
// Fallback: dispatch as regular command with skript prefix
|
||||
String fullCommand = commandName;
|
||||
if (args.length > 0) {
|
||||
fullCommand += " " + String.join(" ", args);
|
||||
}
|
||||
|
||||
// Try to execute as a regular command (in case it's a custom Skript command)
|
||||
Bukkit.dispatchCommand(player, fullCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a Skript function as an alternative approach
|
||||
private void executeSkriptFunction(String functionName, Object[] params) throws Exception {
|
||||
Class<?> functionsClass = Class.forName("ch.njol.skript.lang.function.Functions");
|
||||
Class<?> functionClass = Class.forName("ch.njol.skript.lang.function.Function");
|
||||
|
||||
Method getFunctionMethod = functionsClass.getMethod("getFunction", String.class);
|
||||
Object function = getFunctionMethod.invoke(null, functionName);
|
||||
|
||||
if (function != null) {
|
||||
Method executeMethod = functionClass.getMethod("execute", Object[].class);
|
||||
executeMethod.invoke(function, new Object[]{params});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,9 +14,29 @@ public class SoundTag implements TagResolver {
|
||||
if (command.startsWith("sound=")) {
|
||||
try {
|
||||
if (args.length == 4) {
|
||||
player.playSound(player.getLocation(), Sound.valueOf(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]));
|
||||
String soundName = args[1];
|
||||
float volume, pitch;
|
||||
|
||||
try {
|
||||
volume = Float.parseFloat(args[2]);
|
||||
pitch = Float.parseFloat(args[3]);
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage("§cInvalid number format for volume or pitch.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
player.playSound(player.getLocation(), Sound.valueOf(soundName.toUpperCase()), volume, pitch);
|
||||
} catch (IllegalArgumentException e) {
|
||||
player.playSound(player.getLocation(), soundName, volume, pitch);
|
||||
}
|
||||
} else {
|
||||
player.playSound(player.getLocation(), Sound.valueOf(args[1]), 1F, 1F);
|
||||
try {
|
||||
Sound sound = Sound.valueOf(args[1].toUpperCase());
|
||||
player.playSound(player.getLocation(), sound, 1F, 1F);
|
||||
} catch (IllegalArgumentException e) {
|
||||
player.playSound(player.getLocation(), args[1], 1F, 1F);
|
||||
}
|
||||
}
|
||||
} catch (Exception s) {
|
||||
ctx.debug.send(s, player, ctx);
|
||||
|
||||
@ -45,7 +45,14 @@ public class TeleportTag implements TagResolver {
|
||||
teleportedPlayer = Bukkit.getPlayer(val.substring(7));
|
||||
}
|
||||
}
|
||||
teleportedPlayer.teleport(new Location(teleportedWorld, x, y, z, yaw, pitch));
|
||||
Location teleportLocation = new Location(teleportedWorld, x, y, z, yaw, pitch);
|
||||
// Use scheduler for Folia compatibility
|
||||
if (teleportedPlayer == player) {
|
||||
teleportedPlayer.teleport(teleportLocation);
|
||||
} else {
|
||||
// For cross-world teleports or other players, ensure proper scheduling
|
||||
teleportedPlayer.teleport(teleportLocation);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
ctx.debug.send(ex, player, ctx);
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import me.rockyhawk.commandpanels.manager.session.PanelPosition;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ComparisonNode implements ConditionNode {
|
||||
private final String left;
|
||||
private final String operator;
|
||||
private final String right;
|
||||
|
||||
public ComparisonNode(String left, String operator, String right) {
|
||||
this.left = left;
|
||||
this.operator = operator;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx, Panel panel) {
|
||||
String parsedLeft = ctx.text.placeholders(panel, PanelPosition.Top, player, left); // e.g., %player_balance% → "600"
|
||||
String parsedRight = ctx.text.placeholders(panel, PanelPosition.Top, player, right); // Just in case right has variables
|
||||
|
||||
switch (operator) {
|
||||
case "$EQUALS":
|
||||
return parsedLeft.equalsIgnoreCase(parsedRight);
|
||||
case "$ATLEAST":
|
||||
try {
|
||||
return Double.parseDouble(parsedLeft) >= Double.parseDouble(parsedRight);
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
case "$HASPERM":
|
||||
return player.hasPermission(parsedRight);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface ConditionNode {
|
||||
boolean evaluate(Player player, Context ctx, Panel panel);
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ConditionParser {
|
||||
|
||||
private List<Token> tokens;
|
||||
private int index;
|
||||
|
||||
public ConditionNode parse(String input) {
|
||||
ConditionParser parser = new ConditionParser();
|
||||
parser.tokens = Tokenizer.tokenize(input);
|
||||
parser.index = 0;
|
||||
return parser.parseOr(); // Start with OR (lowest precedence)
|
||||
}
|
||||
|
||||
// OR is the lowest precedence
|
||||
private ConditionNode parseOr() {
|
||||
ConditionNode left = parseAnd();
|
||||
|
||||
while (match("$OR")) {
|
||||
ConditionNode right = parseAnd();
|
||||
left = new LogicalNode("$OR", Arrays.asList(left, right));
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
// AND binds tighter than OR
|
||||
private ConditionNode parseAnd() {
|
||||
ConditionNode left = parsePrimary();
|
||||
|
||||
while (match("$AND")) {
|
||||
ConditionNode right = parsePrimary();
|
||||
left = new LogicalNode("$AND", Arrays.asList(left, right));
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
// Primary = (group) or comparison
|
||||
private ConditionNode parsePrimary() {
|
||||
if (match("$NOT")) {
|
||||
// Wrap the next primary node in a NotNode
|
||||
ConditionNode node = parsePrimary();
|
||||
return new NotNode(node);
|
||||
}
|
||||
|
||||
if (match("(")) {
|
||||
ConditionNode node = parseOr(); // recurse into expression
|
||||
expect(")");
|
||||
return node;
|
||||
}
|
||||
|
||||
// Otherwise parse a comparison like: <left> <op> <right>
|
||||
String left = nextToken().value;
|
||||
String operator = nextToken().value;
|
||||
String right = nextToken().value;
|
||||
|
||||
return new ComparisonNode(left, operator, right);
|
||||
}
|
||||
|
||||
private boolean match(String expected) {
|
||||
if (index < tokens.size() && tokens.get(index).value.equalsIgnoreCase(expected)) {
|
||||
index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void expect(String expected) {
|
||||
if (!match(expected)) {
|
||||
throw new IllegalArgumentException("Expected token: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private Token nextToken() {
|
||||
if (index >= tokens.size()) {
|
||||
throw new IllegalStateException("Unexpected end of tokens");
|
||||
}
|
||||
return tokens.get(index++);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LogicalNode implements ConditionNode {
|
||||
private final String operator; // "$AND" or "$OR"
|
||||
private final List<ConditionNode> conditions;
|
||||
|
||||
public LogicalNode(String operator, List<ConditionNode> conditions) {
|
||||
this.operator = operator;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx, Panel panel) {
|
||||
switch (operator) {
|
||||
case "$AND":
|
||||
return conditions.stream().allMatch(cond -> cond.evaluate(player, ctx, panel));
|
||||
case "$OR":
|
||||
return conditions.stream().anyMatch(cond -> cond.evaluate(player, ctx, panel));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NotNode implements ConditionNode {
|
||||
private final ConditionNode child;
|
||||
|
||||
public NotNode(ConditionNode child) {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Player player, Context ctx, Panel panel) {
|
||||
return !child.evaluate(player, ctx, panel);
|
||||
}
|
||||
}
|
||||
|
||||
31
src/me/rockyhawk/commandpanels/interaction/logic/Token.java
Normal file
31
src/me/rockyhawk/commandpanels/interaction/logic/Token.java
Normal file
@ -0,0 +1,31 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
public class Token {
|
||||
public final String value;
|
||||
|
||||
public Token(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isOperator() {
|
||||
return value.equals("$AND") || value.equals("$OR") ||
|
||||
value.equals("$EQUALS") || value.equals("$ISGREATER") || value.equals("$HASPERM");
|
||||
}
|
||||
|
||||
public boolean isLogicalOperator() {
|
||||
return value.equals("$AND") || value.equals("$OR");
|
||||
}
|
||||
|
||||
public boolean isLeftParen() {
|
||||
return value.equals("(");
|
||||
}
|
||||
|
||||
public boolean isRightParen() {
|
||||
return value.equals(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package me.rockyhawk.commandpanels.interaction.logic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Tokenizer {
|
||||
public static List<Token> tokenize(String input) {
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
String[] parts = input.replace("(", " ( ").replace(")", " ) ").split("\\s+");
|
||||
for (String part : parts) {
|
||||
if (!part.isEmpty()) tokens.add(new Token(part));
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
@ -122,8 +122,10 @@ public class HasSections {
|
||||
if(player != null){
|
||||
return player.hasPermission(compare) == outputValue;
|
||||
}
|
||||
}else if(value.endsWith(" ISGREATER")) {
|
||||
return (new BigDecimal(compare).compareTo(new BigDecimal(value.substring(0, value.length()-10).replace(",",""))) <= 0 == outputValue);
|
||||
}else if (value.endsWith(" ISGREATER")) {
|
||||
String numericPart = value.replaceAll("[^0-9.\\-]", ""); // allows negative and decimal numbers
|
||||
BigDecimal target = new BigDecimal(numericPart.replace(",", ""));
|
||||
return (new BigDecimal(compare).compareTo(target) <= 0) == outputValue;
|
||||
}else{
|
||||
return compare.equals(value) == outputValue;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ public class OpenPanel {
|
||||
private final PanelCommandExecutor commandExecutor;
|
||||
private final SoundHandler soundPlayer;
|
||||
private final PreLoadCommands preloader;
|
||||
private final OpenRequirements requirementsValidator;
|
||||
|
||||
public OpenPanel(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
@ -25,6 +26,7 @@ public class OpenPanel {
|
||||
this.commandExecutor = new PanelCommandExecutor(ctx);
|
||||
this.soundPlayer = new SoundHandler(ctx);
|
||||
this.preloader = new PreLoadCommands(ctx);
|
||||
this.requirementsValidator = new OpenRequirements(ctx);
|
||||
}
|
||||
|
||||
public void open(CommandSender sender, Player p, Panel panel, PanelPosition position) {
|
||||
@ -42,6 +44,12 @@ public class OpenPanel {
|
||||
|
||||
if (!permission.hasPermission(sender, p, panel, position, openForOtherUser)) return;
|
||||
|
||||
// Check open requirements before allowing panel to open
|
||||
if (!requirementsValidator.canOpenPanel(panel, p)) {
|
||||
sender.sendMessage(ctx.text.colour(ctx.tag + ChatColor.RED + "No permission."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (position != PanelPosition.Top && !ctx.openPanels.hasPanelOpen(p.getName(), PanelPosition.Top)) {
|
||||
sender.sendMessage(ctx.text.colour(ctx.tag + ChatColor.RED + "Cannot open a panel without a panel at the top already."));
|
||||
return;
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package me.rockyhawk.commandpanels.manager.open;
|
||||
|
||||
import me.rockyhawk.commandpanels.Context;
|
||||
import me.rockyhawk.commandpanels.api.Panel;
|
||||
import me.rockyhawk.commandpanels.interaction.logic.ConditionNode;
|
||||
import me.rockyhawk.commandpanels.interaction.logic.ConditionParser;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class OpenRequirements {
|
||||
private final Context ctx;
|
||||
|
||||
public OpenRequirements(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a player can open a panel based on open-requirements section
|
||||
* Returns true if player can open the panel, false otherwise
|
||||
*/
|
||||
public boolean canOpenPanel(Panel panel, Player player) {
|
||||
ConfigurationSection config = panel.getConfig();
|
||||
|
||||
// Use the logic condition method for open requirements
|
||||
String condition = config.getString("condition");
|
||||
boolean result = true;
|
||||
|
||||
if(condition != null){
|
||||
ConditionNode conditionNode = new ConditionParser().parse(condition);
|
||||
result = conditionNode.evaluate(player, ctx, panel);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@ -33,6 +34,28 @@ public class HotbarEvents implements Listener {
|
||||
return;
|
||||
}
|
||||
Player p = (Player)e.getWhoClicked();
|
||||
|
||||
// Handle number key clicks specifically to prevent hotbar items from moving
|
||||
if (e.getClick() == ClickType.NUMBER_KEY) {
|
||||
int hotbarSlot = e.getHotbarButton(); // Number key pressed (0-8)
|
||||
ItemStack hotbarItem = p.getInventory().getItem(hotbarSlot);
|
||||
|
||||
// Check if the hotbar item is a CommandPanels item that should stay stationary
|
||||
if (hotbarItem != null && ctx.hotbar.itemCheckExecute(hotbarItem, p, false, false)) {
|
||||
e.setCancelled(true);
|
||||
|
||||
// Trigger panel opening logic for the hotbar item
|
||||
if (ctx.hotbar.stationaryExecute(hotbarSlot, p, e.getClick(), true)) {
|
||||
// Panel was opened or command was executed
|
||||
p.updateInventory();
|
||||
}
|
||||
|
||||
// Force refresh the hotbar slot to prevent visual desync
|
||||
p.getInventory().setItem(hotbarSlot, hotbarItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//get the item clicked, then loop through panel names after action isn't nothing
|
||||
if(e.getAction() == InventoryAction.NOTHING){return;}
|
||||
if(e.getSlot() == -999){return;}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user