Compare commits

...

22 Commits

Author SHA1 Message Date
XAP3Y
690ac0e6bf CI trigger
All checks were successful
Build CommandPanels plugin / Build-latest-jar (push) Successful in 5m33s
2024-12-22 21:15:18 +01:00
XAP3Y
86aecb856a bungee command support 2024-12-22 21:14:13 +01:00
rockyhawk64
e58adfa9f5 3.21.4.4 2024-12-07 15:36:32 +11:00
rockyhawk64
5473206adf 3.21.4.3 2024-11-14 17:35:38 +11:00
RockyHawk
aced7b6243
Merge pull request #331 from OakLoaf/latest
Made panel downloads from editor async
2024-11-09 12:17:27 +11:00
OakLoaf
98a02f2408 Updated JeffMedia repository 2024-10-28 13:37:08 +00:00
Oak
5f5db64255 Made panel download from editor async 2024-10-20 17:50:31 +01:00
rockyhawk64
9975b2f76a 3.21.4.2 2024-09-27 15:49:58 +10:00
rockyhawk64
768b2a6de9 3.21.4.0 2024-08-23 13:44:21 +10:00
rockyhawk64
8b518e3cb7 3.21.4.0 2024-08-15 22:09:24 +10:00
RockyHawk
372a9c0e86
Merge pull request #321 from TinyTank800/latest
Additional placeholders - name-slot, lore-slot AND hasnoperm=
2024-08-15 13:43:26 +10:00
TinyTank800
3034ecd8f3 Partial attempt at minimessage for everything and placeholders for item slots. 2024-08-13 20:05:10 -07:00
rockyhawk64
15766dca58 3.21.3.4 2024-08-11 12:10:33 +10:00
TinyTank800
42e61009db hasnoperm= addition. If player does not have perm, continue. 2024-08-09 11:02:59 -07:00
TinyTank800
f068a61596 %cp-name-slot% %cp-lore-slot% additions. Useful for has section checks. Lore is formatted with \n as the spacer. 2024-08-09 10:53:50 -07:00
rockyhawk64
a27360f8ff 3.21.3.3 2024-08-07 17:58:28 +10:00
rockyhawk64
6f2a39d613 3.21.3.2 2024-07-11 10:30:51 +10:00
rockyhawk64
6c390a98f2 3.21.3.1 2024-06-21 17:49:33 +10:00
rockyhawk64
9aa6a64f1e 3.21.3.0 2024-06-17 14:46:02 +10:00
rockyhawk64
a8663e41a7 NBT Improvements 2024-06-16 21:36:21 +10:00
rockyhawk64
843ba8b393 3.21.2.5 Jitpack 2024-06-14 15:00:30 +10:00
rockyhawk64
2bc6259e41 3.21.2.5 2024-05-26 12:21:35 +10:00
45 changed files with 1131 additions and 471 deletions

View File

@ -0,0 +1,30 @@
name: Build CommandPanels plugin
run-name: Build CommandPanels plugin
on: [push]
jobs:
Build-latest-jar:
runs-on: ubuntu-latest
steps:
- name: Build | Prepare packages
run: |
apt update; apt-get install software-properties-common -y
wget -O- https://apt.corretto.aws/corretto.key | apt-key add -
add-apt-repository 'deb https://apt.corretto.aws stable main'
apt-get update; apt-get install -y maven java-21-amazon-corretto-jdk
- name: Setup git
run: |
git config --global user.name "Radim Lipovčan"
git config --global user.email "radim@lipovcan.cz"
- name: Check out repository code
uses: actions/checkout@v4
- name: Build | Maven clean build
run: |
mvn clean install && ls -lah && ls */ -lah && ls */* -lah&& ls */*/* -lah
- name: Push | Create release
uses: https://git.lipovcan.cz/Upstream/gitea-release-action.git@v1
with:
files: |-
target/CommandPanels-DEV.jar
- name: Push | Old FTP way to gitea.lipovcan.cz:8081
run: |
curl --insecure --user username:mypass -T ./target/CommandPanels-DEV.jar ftp://192.168.10.133:/

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
# Project exclude paths
/out/
/target/
.idea/
.idea/
.github

View File

@ -6,11 +6,6 @@
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="io.papermc" />
<option name="name" value="io.papermc" />
<option name="url" value="https://papermc.io/repo/repository/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="jitpack.io" />
<option name="name" value="jitpack.io" />
@ -21,11 +16,21 @@
<option name="name" value="jeff-media-public" />
<option name="url" value="https://hub.jeff-media.com/nexus/repository/jeff-media-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="io.papermc" />
<option name="name" value="io.papermc" />
<option name="url" value="https://papermc.io/repo/repository/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="sonatype" />
<option name="name" value="sonatype" />
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="io.papermc" />
<option name="name" value="io.papermc" />
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="codemc-repo" />
<option name="name" value="codemc-repo" />
@ -47,15 +52,40 @@
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="BenCodez Repo" />
<option name="name" value="BenCodez Repo" />
<option name="url" value="https://nexus.bencodez.com/repository/maven-public/" />
<option name="id" value="oraxen" />
<option name="name" value="Oraxen Repository" />
<option name="url" value="https://repo.oraxen.com/#/releases" />
</remote-repository>
<remote-repository>
<option name="id" value="phoenix" />
<option name="name" value="phoenix" />
<option name="url" value="https://nexus.phoenixdevt.fr/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BenCodez Repo" />
<option name="name" value="BenCodez Repo" />
<option name="url" value="https://nexus.bencodez.com/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="central" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="oraxen" />
<option name="name" value="Oraxen Repository" />
<option name="url" value="https://repo.oraxen.com/releases" />
</remote-repository>
<remote-repository>
<option name="id" value="github" />
<option name="name" value="github" />
<option name="url" value="https://maven.pkg.github.com/xap3y/skullcreator" />
</remote-repository>
<remote-repository>
<option name="id" value="jitpack-repo" />
<option name="name" value="jitpack-repo" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="ess-repo" />
<option name="name" value="ess-repo" />
@ -66,6 +96,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="jeff-media-public" />
<option name="name" value="jeff-media-public" />
<option name="url" value="https://repo.jeff-media.com/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />

2
jitpack.yml Normal file
View File

@ -0,0 +1,2 @@
jdk:
- openjdk17

60
pom.xml
View File

@ -4,12 +4,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<groupId>me.rockyhawk.commandpanels</groupId>
<artifactId>CommandPanels</artifactId>
<version>DEV</version>
<properties>
<java.version>17</java.version>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<src.dir>src</src.dir>
</properties>
@ -71,14 +71,14 @@
</build>
<repositories>
<repository>
<id>io.papermc</id>
<url>https://papermc.io/repo/repository/maven-releases/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>io.papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net/</url>
@ -89,11 +89,7 @@
</repository>
<repository>
<id>jeff-media-public</id>
<url>https://hub.jeff-media.com/nexus/repository/jeff-media-public/</url>
</repository>
<repository>
<id>BenCodez Repo</id>
<url>https://nexus.bencodez.com/repository/maven-public/</url>
<url>https://repo.jeff-media.com/public/</url>
</repository>
<repository>
<id>ess-repo</id>
@ -126,7 +122,7 @@
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api</artifactId>
<version>2.12.4-SNAPSHOT</version>
<version>2.14.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -138,7 +134,7 @@
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.13.0</version>
<version>4.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -162,18 +158,12 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.bencodez</groupId>
<artifactId>votingplugin</artifactId>
<version>LATEST</version>
<scope>provided</scope>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.3</version>
<version>2.11.6</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -185,25 +175,13 @@
<dependency>
<groupId>net.Indyuce</groupId>
<artifactId>MMOItems-API</artifactId>
<version>LATEST</version>
<version>6.10-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MythicLib-dist</artifactId>
<version>LATEST</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.Realizedd</groupId>
<artifactId>TokenManager</artifactId>
<version>LATEST</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<version>1.6.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -212,6 +190,12 @@
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.LoneDev6</groupId>
<artifactId>api-itemsadder</artifactId>
<version>3.6.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
@ -223,12 +207,6 @@
<version>2.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>2.2.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>

View File

@ -16,7 +16,6 @@ config:
stop-sound: true
disabled-world-message: true
panel-snooper: false
allow-unsafe-mini-message: false
enable-import-command: false
format:
tag: '&6[&bCommandPanels&6] '
@ -38,7 +37,7 @@ input:
- '&aEnter Input for Command'
- '&cType &4%cp-args% &cto Cancel the command'
hexcodes:
start_tag: '#'
start_tag: '&#'
end_tag: ''
placeholders:
primary:

View File

@ -1,4 +1,4 @@
version: 3.21.2.4
version: 3.21.4.4
main: me.rockyhawk.commandpanels.CommandPanels
name: CommandPanels
author: RockyHawk

View File

@ -1,11 +1,12 @@
package me.rockyhawk.commandpanels;
import com.bencodez.votingplugin.VotingPluginHooks;
import com.google.common.collect.ImmutableMultimap;
import io.lumine.mythic.lib.api.item.NBTItem;
import me.rockyhawk.commandpanels.api.CommandPanelsAPI;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.classresources.ExecuteOpenVoids;
import me.rockyhawk.commandpanels.classresources.GetCustomHeads;
import me.rockyhawk.commandpanels.classresources.MiniMessageUtils;
import me.rockyhawk.commandpanels.classresources.customheads.GetCustomHeads;
import me.rockyhawk.commandpanels.classresources.HasSections;
import me.rockyhawk.commandpanels.classresources.ItemCreation;
import me.rockyhawk.commandpanels.classresources.placeholders.expansion.CpPlaceholderExpansion;
@ -22,6 +23,7 @@ import me.rockyhawk.commandpanels.completetabs.UpdateTabComplete;
import me.rockyhawk.commandpanels.customcommands.Commandpanelcustom;
import me.rockyhawk.commandpanels.datamanager.DebugManager;
import me.rockyhawk.commandpanels.datamanager.PanelDataLoader;
import me.rockyhawk.commandpanels.datamanager.PanelDataPlayerManager;
import me.rockyhawk.commandpanels.editor.*;
import me.rockyhawk.commandpanels.floodgatecp.OpenFloodgateGUI;
import me.rockyhawk.commandpanels.generatepanels.Commandpanelsgenerate;
@ -35,6 +37,7 @@ import me.rockyhawk.commandpanels.ioclasses.nbt.NBTManager;
import me.rockyhawk.commandpanels.ioclasses.legacy.LegacyVersion;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import me.rockyhawk.commandpanels.ioclasses.legacy.PlayerHeads;
import me.rockyhawk.commandpanels.ioclasses.potions.ClassicPotionData;
import me.rockyhawk.commandpanels.ioclasses.potions.LegacyPotionData;
import me.rockyhawk.commandpanels.openpanelsmanager.*;
import me.rockyhawk.commandpanels.openwithitem.HotbarItemLoader;
@ -74,7 +77,6 @@ import java.io.Reader;
import java.util.*;
public class CommandPanels extends JavaPlugin{
public VotingPluginHooks votingPlugin;
public YamlConfiguration config;
public Economy econ = null;
public boolean openWithItem = false; //this will be true if there is a panel with open-with-item
@ -90,17 +92,22 @@ public class CommandPanels extends JavaPlugin{
public CommandRunner commandRunner = new CommandRunner(this);
public PanelDataLoader panelData = new PanelDataLoader(this);
public PanelDataPlayerManager panelDataPlayers = new PanelDataPlayerManager(this);
public Placeholders placeholders = new Placeholders(this);
public DebugManager debug = new DebugManager(this);
public CreateText tex = new CreateText(this);
public HexColours hex = new HexColours(this);
public MiniMessageUtils miniMessage = null;
public ExecuteOpenVoids openVoids = new ExecuteOpenVoids(this);
public ItemCreation itemCreate = new ItemCreation(this);
public HasSections has = new HasSections(this);
public GetCustomHeads customHeads = new GetCustomHeads(this);
public Updater updater = new Updater(this);
public PlayerHeads getHeads = new PlayerHeads(this);
public ClassicPotionData classicPotion = new ClassicPotionData(this);
public LegacyPotionData legacyPotion = new LegacyPotionData(this);
public LegacyVersion legacy = new LegacyVersion(this);
@ -113,7 +120,6 @@ public class CommandPanels extends JavaPlugin{
public InventorySaver inventorySaver = new InventorySaver(this);
public ItemStackSerializer itemSerializer = new ItemStackSerializer(this);
public UserInputUtils inputUtils = new UserInputUtils(this);
public OpenFloodgateGUI floodgateOpenGUI = new OpenFloodgateGUI(this);
public File panelsf = new File(this.getDataFolder() + File.separator + "panels");
public YamlConfiguration blockConfig; //where panel block locations are stored
@ -121,6 +127,9 @@ public class CommandPanels extends JavaPlugin{
public void onEnable() {
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loading...");
//Initialise classes that are not used externally
new OpenFloodgateGUI(this);
//register config files
this.blockConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "blocks.yml"));
panelData.dataConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "data.yml"));
@ -159,6 +168,7 @@ public class CommandPanels extends JavaPlugin{
new Metrics(this, 5097);
this.setupEconomy();
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "my:psb");
Objects.requireNonNull(this.getCommand("commandpanel")).setExecutor(new Commandpanel(this));
Objects.requireNonNull(this.getCommand("commandpanel")).setTabCompleter(new CpTabComplete(this));
@ -189,7 +199,19 @@ public class CommandPanels extends JavaPlugin{
this.getServer().getPluginManager().registerEvents(new legacyPlayerEvent(this), this);
}
try {
// Check all the minimessage classes exist before loading
Class.forName("net.kyori.adventure.text.Component");
Class.forName("net.kyori.adventure.text.format.TextDecoration");
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer");
miniMessage = new MiniMessageUtils(this);
} catch (ClassNotFoundException ignore) {
//do not initialise miniMessage
}
this.getServer().getPluginManager().registerEvents(inputUtils, this);
this.getServer().getPluginManager().registerEvents(panelDataPlayers, this);
this.getServer().getPluginManager().registerEvents(new UtilsPanelsLoader(this), this);
this.getServer().getPluginManager().registerEvents(new GenUtils(this), this);
this.getServer().getPluginManager().registerEvents(new ItemFallManager(this), this);
@ -239,10 +261,6 @@ public class CommandPanels extends JavaPlugin{
if (!Bukkit.getVersion().contains("1.8")) {
this.getServer().getPluginManager().registerEvents(new SwapItemEvent(this), this);
}
//if VotingPlugin is enabled
if (getServer().getPluginManager().isPluginEnabled("VotingPlugin")) {
votingPlugin= VotingPluginHooks.getInstance();
}
//if plugin ChestSort is enabled
if(getServer().getPluginManager().isPluginEnabled("ChestSort")){
this.getServer().getPluginManager().registerEvents(new UtilsChestSortEvent(this), this);
@ -281,6 +299,9 @@ public class CommandPanels extends JavaPlugin{
//do hotbar items
hotbar.reloadHotbarSlots();
//load all known players for data
panelDataPlayers.reloadAllPlayers();
//add custom charts bStats
Metrics metrics = new Metrics(this, 5097);
metrics.addCustomChart(new SingleLineChart("panels_amount", () -> {
@ -328,8 +349,8 @@ public class CommandPanels extends JavaPlugin{
assert renamedMeta != null;
//hiding attributes will add an NBT tag
if(hideAttributes) {
renamedMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
renamedMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
renamedMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
//HIDE_ADDITIONAL_TOOLTIP was added into 1.20.5 api
if(legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_21) ||
(legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_20) && legacy.MINOR_VERSION >= 5)){
@ -348,6 +369,10 @@ public class CommandPanels extends JavaPlugin{
if(legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_17)){
renamedMeta.addItemFlags(ItemFlag.HIDE_DYE);
}
//setAttributeModifiers was added into 1.14 api
if(legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_14)){
renamedMeta.setAttributeModifiers(ImmutableMultimap.of());
}
}
if (customName != null) {
renamedMeta.setDisplayName(customName);

View File

@ -87,23 +87,24 @@ public class Utils implements Listener {
panel = plugin.openPanels.getOpenPanel(p.getName(),position);
//this loops through all the items in the panel
boolean foundSlot = false;
for(String slot : Objects.requireNonNull(panel.getConfig().getConfigurationSection("item")).getKeys(false)){
String foundSlot = null;
for(String item : Objects.requireNonNull(panel.getConfig().getConfigurationSection("item")).getKeys(false)){
String slot = plugin.tex.placeholdersNoColour(panel, position, p, item);
if (slot.equals(Integer.toString(clickedSlot))) {
foundSlot = true;
foundSlot = item;
break;
}
}
if(!foundSlot){
if(foundSlot == null){
e.setCancelled(true);
return;
}
//get the section of the slot that was clicked
String section = plugin.has.hasSection(panel,position,panel.getConfig().getConfigurationSection("item." + clickedSlot), p);
String section = plugin.has.hasSection(panel,position,panel.getConfig().getConfigurationSection("item." + foundSlot), p);
if(panel.getConfig().contains("item." + clickedSlot + section + ".itemType")){
if(panel.getConfig().getStringList("item." + clickedSlot + section + ".itemType").contains("placeable")){
if(panel.getConfig().contains("item." + foundSlot + section + ".itemType")){
if(panel.getConfig().getStringList("item." + foundSlot + section + ".itemType").contains("placeable")){
//skip if the item is a placeable
e.setCancelled(false);
return;
@ -115,13 +116,13 @@ public class Utils implements Listener {
p.updateInventory();
//if an item has an area for input instead of commands
if(panel.getConfig().contains("item." + clickedSlot + section + ".player-input")) {
plugin.inputUtils.playerInput.put(p,new PlayerInput(panel,panel.getConfig().getStringList("item." + clickedSlot + section + ".player-input"),e.getClick()));
if(panel.getConfig().contains("item." + foundSlot + section + ".player-input")) {
plugin.inputUtils.playerInput.put(p,new PlayerInput(panel,panel.getConfig().getStringList("item." + foundSlot + section + ".player-input"),e.getClick()));
plugin.inputUtils.sendMessage(panel,position,p);
}
if(panel.getConfig().contains("item." + clickedSlot + section + ".commands")) {
List<String> commands = panel.getConfig().getStringList("item." + clickedSlot + section + ".commands");
if(panel.getConfig().contains("item." + foundSlot + section + ".commands")) {
List<String> commands = panel.getConfig().getStringList("item." + foundSlot + section + ".commands");
if (!commands.isEmpty()) {
for (int i = 0; commands.size() > i; i++) {
try {
@ -130,9 +131,9 @@ public class Utils implements Listener {
commands.set(i, commands.get(i).replaceAll("%cp-clicked%", "AIR"));
}
}
if (panel.getConfig().contains("item." + clickedSlot + section + ".multi-paywall")) {
if (panel.getConfig().contains("item." + foundSlot + section + ".multi-paywall")) {
plugin.commandRunner.runMultiPaywall(panel,position,p,
panel.getConfig().getStringList("item." + clickedSlot + section + ".multi-paywall"),
panel.getConfig().getStringList("item." + foundSlot + section + ".multi-paywall"),
commands,e.getClick());
} else {
plugin.commandRunner.runCommands(panel, position, p, commands, e.getClick());

View File

@ -99,7 +99,7 @@ public class Panel{
}
try {
//add NBT to item and return the ItemStack
return plugin.nbt.setNBT(s, "CommandPanelsHotbar", panelName + ":" + slot);
return plugin.nbt.setNBT(s, "CommandPanelsHotbar", "string_" + panelName + ":" + slot);
}catch(Exception e) {
//return air if null
return new ItemStack(Material.AIR);

View File

@ -9,9 +9,7 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.*;
public class HasSections {
CommandPanels plugin;
@ -19,74 +17,85 @@ public class HasSections {
plugin = pl;
}
public String hasSection(Panel panel, PanelPosition position, ConfigurationSection cf, Player p){
for (int count = 0; cf.getKeys(false).size() > count; count++) {
String setName;
if(cf.isSet("has" + count)) {
setName = "has" + count;
}else{
continue;
public String hasSection(Panel panel, PanelPosition position, ConfigurationSection cf, Player p) {
// Use a TreeMap to automatically sort the sections by the extracted number.
Map<Integer, String> sortedSections = new TreeMap<>();
// Loop through the section names and filter for the ones starting with "has".
for (String key : cf.getKeys(false)) {
if (!cf.isConfigurationSection(key)) continue;
// Check if the section starts with "has" and is followed by a number.
if (key.startsWith("has")) {
try {
// Extract the number after "has" and put it in the map for sorting.
int number = Integer.parseInt(key.substring(3));
sortedSections.put(number, key);
} catch (NumberFormatException ignore) {
// If the section name doesn't have a valid number after "has", skip it.
}
}
}
boolean endProcess = true;
//loop through possible values and compares for hypothetical and operators
for (int a = 0; cf.getConfigurationSection(setName).getKeys(false).size() > a; a++) {
if(cf.isSet(setName + ".value" + a) && cf.isSet(setName + ".compare" + a)){
//ensure the endProcess variable has been reset for another operation
endProcess = true;
//get the values of this statement
String value;
String compare;
try {
value = ChatColor.stripColor(plugin.tex.placeholders(panel, position, p, cf.getString(setName + ".value" + a)));
compare = ChatColor.stripColor(plugin.tex.placeholders(panel, position, p, cf.getString(setName + ".compare" + a)));
}catch (Exception e) {
//if errors getting text return
plugin.debug(e,p);
return "";
}
for (String hasSection : sortedSections.values()) {
if (!cf.isConfigurationSection(hasSection)) continue;
String operator = "AND";
if(compare.endsWith(" OR")){
compare = compare.substring(0, compare.length()-3);
operator = "OR";
}else if(compare.endsWith(" AND")){
compare = compare.substring(0, compare.length()-4);
}
ConfigurationSection currentSection = cf.getConfigurationSection(hasSection);
int numberOfConditions = currentSection.getKeys(false).size();
//list of values with the or operator
HashSet<String> values = doOperators(new HashSet<>(Collections.singletonList(value)));
//go through all values with the or operator
for(String val : values){
if (hasProcess(setName, val, compare, p)) {
endProcess = false;
//if it is true and it is OR, there is no need to check the next value in the line
if(operator.equals("OR")){
a++;
}
}
}
if(endProcess){
//check if the operator link between the next value/compare is OR
if(operator.equals("OR")){
//I can just continue because the algorithm already assumes the last sequence was true
endProcess = false;
continue;
}
Boolean currentBlockResult = null; // This will store the result of the current block (a set of conditions combined by AND or OR).
String previousOperator = "AND"; // Default logical operator to start with.
for (int a = 0; a < numberOfConditions; a++) {
if (!currentSection.isSet("value" + a) || !currentSection.isSet("compare" + a)) {
continue;
}
String value = ChatColor.stripColor(plugin.tex.placeholders(panel, position, p, currentSection.getString("value" + a)));
String compare = ChatColor.stripColor(plugin.tex.placeholders(panel, position, p, currentSection.getString("compare" + a)));
String operator = "AND"; // Default operator for the current condition.
if (compare.endsWith(" OR")) {
compare = compare.substring(0, compare.length() - 3);
operator = "OR";
} else if (compare.endsWith(" AND")) {
compare = compare.substring(0, compare.length() - 4);
}
HashSet<String> values = doOperators(new HashSet<>(Collections.singletonList(value)));
boolean localResult = false; // This tracks the result of the current condition.
for (String val : values) {
if (hasProcess(val, compare)) {
localResult = true;
break;
}
}
if (currentBlockResult == null) {
// Initialize the result of the block with the result of the first condition.
currentBlockResult = localResult;
} else {
// Combine the result of the current condition with the block result based on the previous operator.
if (previousOperator.equals("AND")) {
currentBlockResult = currentBlockResult && localResult;
} else if (previousOperator.equals("OR")) {
currentBlockResult = currentBlockResult || localResult;
}
}
previousOperator = operator; // Update the operator for the next condition.
}
//if the has section is false move to the next has section
if(endProcess){
continue;
if (currentBlockResult != null && currentBlockResult) {
// If the result of this section is true, check nested sections.
return "." + hasSection + hasSection(panel, position, currentSection, p);
}
//proceed if none of the values were false
return "." + setName + hasSection(panel, position, cf.getConfigurationSection(setName), p);
// If the result is false, continue to the next 'has' section.
}
return "";
}
private HashSet<String> doOperators(HashSet<String> value){
for(String val : value){
if(val.contains(" OR ")){
@ -98,7 +107,7 @@ public class HasSections {
return value;
}
private boolean hasProcess(String setName, String value, String compare,Player p){
private boolean hasProcess(String value, String compare){
//check to see if the value should be reversed
boolean outputValue = true;
if(value.startsWith("NOT ")){
@ -107,18 +116,16 @@ public class HasSections {
}
//the current has section with all the functions implemented inside it
if(setName.startsWith("has")) {
if(value.endsWith(" HASPERM")) {
String playername = value.substring(0, value.length()-8);
Player player = Bukkit.getPlayerExact(playername);
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{
return compare.equals(value) == outputValue;
if(value.endsWith(" HASPERM")) {
String playername = value.substring(0, value.length()-8);
Player player = Bukkit.getPlayerExact(playername);
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{
return compare.equals(value) == outputValue;
}
return false;
}

View File

@ -1,8 +1,10 @@
package me.rockyhawk.commandpanels.classresources;
import dev.lone.itemsadder.api.CustomStack;
import me.arcaniax.hdb.api.HeadDatabaseAPI;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.classresources.customheads.SavedCustomHead;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import net.Indyuce.mmoitems.MMOItems;
@ -26,6 +28,7 @@ import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.bukkit.potion.PotionType;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
@ -94,6 +97,50 @@ public class ItemCreation {
}
}
//Oraxen support, uses itemID (eg, oraxen= coin)
if (matraw.split("\\s")[0].equalsIgnoreCase("oraxen=")) {
String itemID = matraw.split("\\s")[1];
try {
// Load the OraxenItems class
Class<?> oraxenItemsClass = Class.forName("io.th0rgal.oraxen.api.OraxenItems");
// Retrieve the 'getItemById' method from the OraxenItems class
Method getItemByIdMethod = oraxenItemsClass.getMethod("getItemById", String.class);
getItemByIdMethod.setAccessible(true);
// Invoke the 'getItemById' method with the itemID
Object oraxenItem = getItemByIdMethod.invoke(null, itemID); // static method, so pass 'null'
// Ensure that the method returned a valid Oraxen item
if (oraxenItem != null) {
// Now we need to invoke 'getReferenceClone' on the OraxenItem object
Method getReferenceCloneMethod = oraxenItem.getClass().getMethod("getReferenceClone");
getReferenceCloneMethod.setAccessible(true);
ItemStack stack = (ItemStack) getReferenceCloneMethod.invoke(oraxenItem);
// Check if stack is not null
if (stack != null) {
s = stack;
normalCreation = false;
}
}
} catch (Exception e) {
plugin.debug(e, null);
// Handle the error or inform the player
}
}
//ItemsAdder support, needs namespaceID (eg, itemsadder= money:coin)
if (matraw.split("\\s")[0].equalsIgnoreCase("itemsadder=")) {
String namespaceID = matraw.split("\\s")[1];
CustomStack stack = CustomStack.getInstance(namespaceID);
if(stack != null) {
s = stack.getItemStack().clone();
normalCreation = false;
}
}
//creates custom MMOItems items
if(matraw.split("\\s")[0].equalsIgnoreCase("mmo=") && plugin.getServer().getPluginManager().isPluginEnabled("MMOItems")){
String itemType = matraw.split("\\s")[1];
@ -191,10 +238,12 @@ public class ItemCreation {
}
if(itemSection.contains("nbt")){
plugin.nbt.applyNBTRecursively("", itemSection, s, p, panel, position);
//ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position
plugin.nbt.applyNBTRecursively(s, itemSection.getConfigurationSection("nbt"), p, panel, position);
//plugin.nbt.applyNBTRecursively("", itemSection.getConfigurationSection("nbt"), s, p, panel, position);
}
if(addNBT){
plugin.nbt.setNBT(s, "CommandPanelsItem", "true");
plugin.nbt.setNBT(s, "CommandPanelsItem","boolean_" + "true");
}
if (itemSection.contains("enchanted")) {
@ -281,7 +330,11 @@ public class ItemCreation {
//potion legacy or current
if(plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_19) ||
(plugin.legacy.MAJOR_VERSION == MinecraftVersions.v1_20 && plugin.legacy.MINOR_VERSION <= 4)){
plugin.legacyPotion.applyPotionEffect(p,s,effectType);
if(plugin.legacy.MAJOR_VERSION.equals(MinecraftVersions.v1_8)){
plugin.classicPotion.applyPotionEffect(p, s, effectType);
}else {
plugin.legacyPotion.applyPotionEffect(p, s, effectType);
}
}else{
try {
PotionMeta potionMeta = (PotionMeta)s.getItemMeta();
@ -297,6 +350,18 @@ public class ItemCreation {
}
}
}
if(itemSection.contains("potion-color")){
if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_11)){
String[] rgb = Objects.requireNonNull(itemSection.getString("potion-color")).split(",");
Color color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
PotionMeta potionMeta = (PotionMeta)s.getItemMeta();
assert potionMeta != null;
potionMeta.setColor(color);
s.setItemMeta(potionMeta);
}
}
if (itemSection.contains("damage")) {
//change the damage amount (placeholders accepted)
//if the damage is not unbreakable and should be a value
@ -313,15 +378,15 @@ public class ItemCreation {
ItemMeta unbreak = s.getItemMeta();
unbreak.setUnbreakable(true);
s.setItemMeta(unbreak);
}
try {
Damageable itemDamage = (Damageable) s.getItemMeta();
itemDamage.setDamage(Integer.parseInt(Objects.requireNonNull(plugin.tex.placeholders(panel,position,p, itemSection.getString("damage")))));
s.setItemMeta((ItemMeta) itemDamage);
} catch (Exception e) {
plugin.debug(e, p);
p.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.error") + " damage: " + itemSection.getString("damage")));
}else {
try {
Damageable itemDamage = (Damageable) s.getItemMeta();
itemDamage.setDamage(Integer.parseInt(Objects.requireNonNull(plugin.tex.placeholders(panel, position, p, itemSection.getString("damage")))));
s.setItemMeta((ItemMeta) itemDamage);
} catch (Exception e) {
plugin.debug(e, p);
p.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.error") + " damage: " + itemSection.getString("damage")));
}
}
}
}
@ -415,14 +480,7 @@ public class ItemCreation {
}
if(plugin.getHeads.ifSkullOrHead(cont.getType().toString())){
if(!Objects.requireNonNull(file.getString("panels." + panelName + ".item." + i + ".material")).contains("%") && !Objects.requireNonNull(file.getString("panels." + panelName + ".item." + i + ".material")).contains("=")) {
SkullMeta meta = (SkullMeta) cont.getItemMeta();
if (plugin.customHeads.getHeadBase64(cont) != null) {
//inject base64 here, disable for legacy as is not working
file.set("panels." + panelName + ".item." + i + ".material", "cps= " + plugin.customHeads.getHeadBase64(cont));
} else{
//return blank head
file.set("panels." + panelName + ".item." + i + ".material", plugin.getHeads.playerHeadString());
}
file.set("panels." + panelName + ".item." + i + ".material", plugin.getHeads.playerHeadString());
}
}
try {

View File

@ -0,0 +1,60 @@
package me.rockyhawk.commandpanels.classresources;
import me.rockyhawk.commandpanels.CommandPanels;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.List;
import java.util.stream.Collectors;
public class MiniMessageUtils {
CommandPanels plugin;
public MiniMessageUtils(CommandPanels pl) {
this.plugin = pl;
}
/*
MiniMessage is used here as an alternative to the standard Minecraft colour codes &
As MiniMessage does not accept legacy colour codes at any point, which is
a possibility when using Minecraft colour codes, running a try/catch allows for
MiniMessage to be used anywhere and ignored when legacy colour codes are found.
*/
public String doMiniMessageLegacy(String string) {
MiniMessage miniMessage = MiniMessage.miniMessage();
try {
Component component = miniMessage.deserialize(string);
return LegacyComponentSerializer.builder()
.character('&')
.hexColors()
.build()
.serialize(component);
}catch (Exception e){
return string;
}
}
public List<String> doMiniMessageLegacy(List<String> strings) {
return strings.stream()
.map(this::doMiniMessageLegacy)
.collect(Collectors.toList());
}
public Component doMiniMessage(String string) {
LegacyComponentSerializer legacyComponentSerializer = LegacyComponentSerializer.builder().hexColors().character('&').build();
Component component = legacyComponentSerializer.deserialize(string.replace('§', '&'));
return MiniMessage.miniMessage().deserialize(MiniMessage.miniMessage().serialize(component.decoration(TextDecoration.ITALIC, false))
.replace("\\<", "<").replace("\\", "").replace("\n", "<br>")).decoration(TextDecoration.ITALIC, false);
}
public List<Component> doMiniMessage(List<String> strings) {
return strings.stream()
.map(this::doMiniMessage)
.collect(Collectors.toList());
}
}

View File

@ -1,11 +0,0 @@
package me.rockyhawk.commandpanels.classresources;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
public class SerializerUtils {
public static Component serializeText(String msg){
return MiniMessage.miniMessage().deserialize(msg);
}
}

View File

@ -0,0 +1,34 @@
package me.rockyhawk.commandpanels.classresources.customheads;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.classresources.customheads.methods.CustomHeadGameProfile;
import me.rockyhawk.commandpanels.classresources.customheads.methods.CustomHeadPlayerProfile;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import org.bukkit.inventory.ItemStack;
public class GetCustomHeads {
CustomHeadGameProfile gameProfileHeadClass;
CustomHeadPlayerProfile playerProfileHeadClass;
CommandPanels plugin;
public GetCustomHeads(CommandPanels pl) {
this.plugin = pl;
gameProfileHeadClass = new CustomHeadGameProfile(pl);
playerProfileHeadClass = new CustomHeadPlayerProfile();
}
public ItemStack getCustomHead(String base64){
if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_18)){
return playerProfileHeadClass.getCustomHead(base64);
}else{
return gameProfileHeadClass.getCustomHead(base64);
}
}
public ItemStack getPlayerHead(String playerName){
if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_21)){
return playerProfileHeadClass.getPlayerHead(playerName);
}else{
return gameProfileHeadClass.getPlayerHead(playerName);
}
}
}

View File

@ -0,0 +1,17 @@
package me.rockyhawk.commandpanels.classresources.customheads;
import org.bukkit.inventory.ItemStack;
public class SavedCustomHead {
public String base64;
public ItemStack headItem;
public boolean isValid; // true if the head was successfully fetched, false otherwise
public long lastAttempt; // timestamp of the last attempt
public SavedCustomHead(ItemStack head, String base64value, boolean isValidAttempt) {
base64 = base64value;
headItem = head;
isValid = isValidAttempt;
lastAttempt = System.currentTimeMillis();
}
}

View File

@ -1,10 +1,10 @@
package me.rockyhawk.commandpanels.classresources;
package me.rockyhawk.commandpanels.classresources.customheads.methods;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import me.arcaniax.hdb.api.HeadDatabaseAPI;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.classresources.customheads.SavedCustomHead;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@ -20,52 +20,13 @@ import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class GetCustomHeads {
public class CustomHeadGameProfile {
CommandPanels plugin;
public GetCustomHeads(CommandPanels pl) {
public CustomHeadGameProfile(CommandPanels pl) {
this.plugin = pl;
}
//will not go above 2000 elements in the list which is roughly 270 KB RAM usage
public HashMap<String, String> playerHeadTextures = new HashMap<>();
public String getHeadBase64(ItemStack head) {
if (plugin.getHeads.ifSkullOrHead(head.getType().toString()) && head.hasItemMeta()) {
//check if the head is a HeadDatabase head first
if(plugin.getServer().getPluginManager().isPluginEnabled("HeadDatabase")) {
HeadDatabaseAPI api = new HeadDatabaseAPI();
try {
String base64 = api.getBase64(head);
if(base64 != null){
return base64;
}
} catch (Exception ignore) {}
}
//try getting Base64 of a custom head
if (!(head.getItemMeta() instanceof SkullMeta)) return null;
SkullMeta meta = (SkullMeta) head.getItemMeta();
if (meta == null) return null;
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
GameProfile profile = (GameProfile) profileField.get(meta);
Collection<Property> textures = profile.getProperties().get("textures");
if (!textures.isEmpty()) {
// Directly accessing fields within the Property object via reflection.
Property textureProperty = textures.iterator().next();
Field valueField = textureProperty.getClass().getDeclaredField("value");
valueField.setAccessible(true);
return (String) valueField.get(textureProperty);
}
} catch (Exception error) {
plugin.debug(error, null);
}
}
return null;
}
public HashMap<String, SavedCustomHead> savedCustomHeads = new HashMap<>();
//getting the head from a Player Name
public ItemStack getPlayerHead(String name) {
@ -75,8 +36,14 @@ public class GetCustomHeads {
}
//get texture if already cached
if(playerHeadTextures.containsKey(name)) {
return getCustomHead(playerHeadTextures.get(name));
if (savedCustomHeads.containsKey(name)) {
if (!savedCustomHeads.get(name).isValid && (System.currentTimeMillis() - savedCustomHeads.get(name).lastAttempt) < 60000) {
// If the last attempt was less than 60 seconds ago and was invalid, return null or a default item
return new ItemStack(Material.valueOf(plugin.getHeads.playerHeadString()));
}
if(savedCustomHeads.get(name).isValid) {
return savedCustomHeads.get(name).headItem; // Return cached item if valid
}
}
//create ItemStack
@ -93,7 +60,7 @@ public class GetCustomHeads {
if(plugin.debug.consoleDebug){
plugin.getServer().getConsoleSender().sendMessage(plugin.tex.colour(plugin.tag +
ChatColor.WHITE +
"Attempting to Download & Cache Head Texture for " + name));
"Download & Cache Head Texture for " + name));
}
// Fetch the player UUID from the Mojang API
@ -115,20 +82,47 @@ public class GetCustomHeads {
String valueReader = new Scanner(texturesConnection.getInputStream(),
StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
String value = valueReader.split("\"value\" : \"")[1].split("\"")[0];
playerHeadTextures.put(name, value);
// Once the API call is finished, update the ItemStack on the main thread
Bukkit.getScheduler().runTask(plugin, () -> {
itemStack.setItemMeta(getCustomHead(value).getItemMeta());
itemStack.setItemMeta(getCustomHead(name, value).getItemMeta());
savedCustomHeads.put(name, new SavedCustomHead(itemStack, value, true));
});
} catch (Exception ignore) {
// Ignore as errors should be skipped and no need to show in console
Bukkit.getScheduler().runTask(plugin, () -> {
//do not overwrite a valid cached head
if(savedCustomHeads.containsKey(name) && savedCustomHeads.get(name).isValid){
return;
}
savedCustomHeads.put(name, new SavedCustomHead(null, null, false)); // Mark as invalid
});
}
});
return itemStack;
}
//will also use cached heads feature to get heads if player name is provided
public ItemStack getCustomHead(String playerName, String b64stringtexture) {
//check for any saved heads
if(savedCustomHeads.containsKey(playerName)) {
if (savedCustomHeads.get(playerName).base64 != null) {
return savedCustomHeads.get(playerName).headItem;
}
savedCustomHeads.get(playerName).isValid = false;
}
//clear cached textures list until length limit is reached
Iterator<Map.Entry<String, SavedCustomHead>> iterator = savedCustomHeads.entrySet().iterator();
while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
iterator.next(); // Move to next entry
iterator.remove(); // Remove the entry
}
//if saved head is not found from player name, get head manually
return getCustomHead(b64stringtexture);
}
//used to get heads from Base64 Textures
@SuppressWarnings("deprecation")
public ItemStack getCustomHead(String b64stringtexture) {

View File

@ -0,0 +1,118 @@
package me.rockyhawk.commandpanels.classresources.customheads.methods;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.profile.PlayerProfile;
import org.bukkit.profile.PlayerTextures;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
public class CustomHeadPlayerProfile {
//cached itemstacks stored for access
public HashMap<String, ItemStack> savedCustomHeads = new HashMap<>();
//Using the PlayerProfile API for getting custom heads
public ItemStack getCustomHead(String base64Texture) {
//check for any saved heads
if(savedCustomHeads.containsKey(base64Texture)) {
return savedCustomHeads.get(base64Texture);
}
//clear cached textures list until length limit is reached
Iterator<Map.Entry<String, ItemStack>> iterator = savedCustomHeads.entrySet().iterator();
while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
iterator.next(); // Move to next entry
iterator.remove(); // Remove the entry
}
// Create a new player head ItemStack
ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1);
SkullMeta skullMeta = (SkullMeta) skull.getItemMeta();
// Create a new PlayerProfile
UUID uuid = UUID.randomUUID(); // Unique ID for the profile
PlayerProfile profile = Bukkit.createPlayerProfile(uuid);
// Decode the base64 texture and extract the texture URL
String decodedTexture = extractSkinUrlFromBase64(base64Texture);
// Set the skin URL using PlayerTextures
PlayerTextures textures = profile.getTextures();
try {
// Using a URL object for the texture
textures.setSkin(new URL(decodedTexture));
} catch (MalformedURLException ignore) {} // Base64 has no URL, ignore
// Apply the textures to the profile
profile.setTextures(textures);
// Apply the PlayerProfile to the SkullMeta
skullMeta.setOwnerProfile(profile);
// Set the modified SkullMeta back to the ItemStack
skull.setItemMeta(skullMeta);
savedCustomHeads.put(base64Texture, skull);
return skull;
}
// New method to get a player head by player name
public ItemStack getPlayerHead(String playerName) {
//check for any saved heads
if(savedCustomHeads.containsKey(playerName)) {
return savedCustomHeads.get(playerName);
}
//clear cached textures list until length limit is reached
Iterator<Map.Entry<String, ItemStack>> iterator = savedCustomHeads.entrySet().iterator();
while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
iterator.next(); // Move to next entry
iterator.remove(); // Remove the entry
}
// Create a new player head ItemStack
ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1);
SkullMeta skullMeta = (SkullMeta) skull.getItemMeta();
// Get the OfflinePlayer object for the provided player name
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
// Create a PlayerProfile from the player's UUID
UUID playerUUID = offlinePlayer.getUniqueId();
PlayerProfile profile = Bukkit.createPlayerProfile(playerUUID);
// Apply the PlayerProfile to the SkullMeta
skullMeta.setOwnerProfile(profile);
// Set the modified SkullMeta back to the ItemStack
skull.setItemMeta(skullMeta);
savedCustomHeads.put(playerName, skull);
return skull;
}
// Helper method to extract the skin URL from the base64 texture
private String extractSkinUrlFromBase64(String base64Texture) {
// Decode the base64 string
byte[] decodedBytes = Base64.getDecoder().decode(base64Texture);
String decodedString = new String(decodedBytes);
// Parse the decoded string as JSON
JsonObject jsonObject = JsonParser.parseString(decodedString).getAsJsonObject();
// Navigate to "textures" -> "SKIN" -> "url"
JsonObject textures = jsonObject.getAsJsonObject("textures");
JsonObject skin = textures.getAsJsonObject("SKIN");
// Return the URL if it exists
return skin.has("url") ? skin.get("url").getAsString() : null;
}
}

View File

@ -74,7 +74,9 @@ public class CreateText {
//change colour
for(String temp : setpapi){
try {
setpapi.set(tempInt, plugin.hex.translateHexColorCodes(ChatColor.translateAlternateColorCodes('&', temp)));
setpapi.set(tempInt,
plugin.hex.translateHexColorCodes(
ChatColor.translateAlternateColorCodes('&', temp)));
}catch(NullPointerException ignore){}
tempInt += 1;
}
@ -84,7 +86,11 @@ public class CreateText {
//regular string papi, but only colours so Player doesn't need to be there
public String colour(String setpapi) {
try {
setpapi = plugin.hex.translateHexColorCodes(ChatColor.translateAlternateColorCodes('&', setpapi));
if(plugin.miniMessage != null){
setpapi = plugin.miniMessage.doMiniMessageLegacy(setpapi);
}
setpapi = ChatColor.translateAlternateColorCodes('&', setpapi);
setpapi = plugin.hex.translateHexColorCodes(setpapi);
return setpapi;
}catch(NullPointerException e){
return setpapi;
@ -105,7 +111,11 @@ public class CreateText {
public String placeholders(Panel panel, PanelPosition position, Player p, String setpapi) {
try {
setpapi = attachPlaceholders(panel,position, p,setpapi);
setpapi = plugin.hex.translateHexColorCodes(ChatColor.translateAlternateColorCodes('&', setpapi));
if(plugin.miniMessage != null){
setpapi = plugin.miniMessage.doMiniMessageLegacy(setpapi);
}
setpapi = ChatColor.translateAlternateColorCodes('&', setpapi);
setpapi = plugin.hex.translateHexColorCodes(setpapi);
return setpapi;
}catch(NullPointerException e){
return setpapi;

View File

@ -35,7 +35,7 @@ public class HexColours {
}
//automatically format regex to escape special characters
public String formatRegex(String path){
private String formatRegex(String path){
String inputString = plugin.config.getString(path);
final String[] metaCharacters = {"\\","^","$","{","}","[","]","(",")",".","*","+","?","|","<",">","-","&","%"};

View File

@ -1,8 +1,6 @@
package me.rockyhawk.commandpanels.classresources.placeholders;
import com.bencodez.votingplugin.VotingPluginHooks;
import com.earth2me.essentials.Essentials;
import me.realized.tokenmanager.api.TokenManager;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
@ -140,19 +138,23 @@ public class Placeholders {
}
}
//placeholder to check if an item has NBT %cp-nbt-slot:key%
if(identifier.startsWith("nbt-")) {
//placeholder to check if an item has NBT %cp-nbt-slot:type:key%
if (identifier.startsWith("nbt-")) {
try {
String slot_key = identifier.replace("nbt-", "");
String value;
value = plugin.nbt.getNBT(p.getOpenInventory().getTopInventory().getItem((int)Double.parseDouble(slot_key.split(":")[0])),slot_key.split(":")[1]);
if(value.isEmpty()){
value = "empty";
}
return value;
}catch (Exception ex){
plugin.debug(ex,p);
return "";
Object value;
value = plugin.nbt.getNBT(
p.getOpenInventory().getTopInventory().getItem(
(int) Double.parseDouble(slot_key.split(":")[0])
),
slot_key.split(":")[2],
slot_key.split(":")[1]
);
// Convert any object type to a string, handle null explicitly if desired
return value == null ? "empty" : String.valueOf(value);
} catch (Exception ex) {
plugin.debug(ex, p);
return ""; // Consider returning "error" or some other indicative string
}
}
@ -207,6 +209,52 @@ public class Placeholders {
return "";
}
}
//get name value from slot in current open inventory (panel)
if(identifier.startsWith("name-")) {
try {
String nameNumber = identifier.replace("name-", "");
String name;
try {
ItemStack item = p.getOpenInventory().getTopInventory().getItem((int)Double.parseDouble(nameNumber));
name = item.getType().toString().replace("_"," ");
if(item.hasItemMeta()){
if(item.getItemMeta().hasDisplayName()){
name = item.getItemMeta().getDisplayName();
}
}
} catch (NullPointerException er) {
name = "";
}
return name;
} catch (Exception ex) {
plugin.debug(ex,p);
return "";
}
}
//get lore value from slot in current open inventory (panel)
if(identifier.startsWith("lore-")) {
try {
String loreNumber = identifier.replace("lore-", "");
String lore = "";
try {
ItemStack item = p.getOpenInventory().getTopInventory().getItem((int)Double.parseDouble(loreNumber));
if(item.hasItemMeta()){
if(item.getItemMeta().hasLore()){
List<String> ListLore = item.getItemMeta().getLore();
for(String list : ListLore){
lore = lore + list + "\n";
}
}
}
} catch (NullPointerException er) {
lore = "";
}
return lore;
} catch (Exception ex) {
plugin.debug(ex,p);
return "";
}
}
//get stack amount from slot in current open inventory (panel)
if(identifier.startsWith("stack-")) {
try {
@ -325,7 +373,7 @@ public class Placeholders {
if(dataPoint.contains(",")){
String dataName = dataPoint.split(",")[0];
String playerName = dataPoint.split(",")[1];
return plugin.panelData.getUserData(Bukkit.getOfflinePlayer(playerName).getUniqueId(),dataName);
return plugin.panelData.getUserData(plugin.panelDataPlayers.getOffline(playerName),dataName);
}else{
return plugin.panelData.getUserData(p.getUniqueId(),dataPoint);
}
@ -334,10 +382,24 @@ public class Placeholders {
return "";
}
}
//returns if a player is found
if(identifier.startsWith("uuid-")) {
try {
String dataPoint = identifier.replace("uuid-", "");
//get data from other user
if(plugin.panelDataPlayers.getOffline(dataPoint) == null){
return "unknown";
}
return plugin.panelDataPlayers.getOffline(dataPoint).toString();
}catch (Exception ex){
plugin.debug(ex,p);
return "";
}
}
//edits data via placeholder execution (will return empty output)
if(identifier.startsWith("setdata-")) {
try {
String point_value = identifier.replace("cp-setdata-", "");
String point_value = identifier.replace("setdata-", "");
String command = "set-data= " + point_value.split(",")[0] + " " + point_value.split(",")[1];
plugin.commandRunner.runCommand(panel,position,p, command);
return "";
@ -399,18 +461,6 @@ public class Placeholders {
} catch (Exception place) {
//skip
}
if (plugin.getServer().getPluginManager().isPluginEnabled("TokenManager")) {
TokenManager api = (TokenManager) Bukkit.getServer().getPluginManager().getPlugin("TokenManager");
assert api != null;
if(identifier.equals("tokenmanager-balance")) {
return Long.toString(api.getTokens(p).orElse(0));
}
}
if (plugin.getServer().getPluginManager().isPluginEnabled("VotingPlugin")) {
if(identifier.equals("votingplugin-points")) {
return String.valueOf(VotingPluginHooks.getInstance().getUserManager().getVotingPluginUser(p).getPoints());
}
}
//end nodes with PlaceHolders
return "";
}

View File

@ -36,11 +36,11 @@ public class Commandpanelsdata implements CommandExecutor {
if (args[1].equalsIgnoreCase("all") || args[1].equalsIgnoreCase("online")) {
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
if (args[1].equalsIgnoreCase("online") && !player.isOnline()) continue;
plugin.panelData.clearData(plugin.panelData.getOffline(player.getName()));
plugin.panelData.clearData(plugin.panelDataPlayers.getOffline(player.getName()));
count++;
}
} else
plugin.panelData.clearData(plugin.panelData.getOffline(args[1]));
plugin.panelData.clearData(plugin.panelDataPlayers.getOffline(args[1]));
if (sendPlayerMessage) {
sender.sendMessage(plugin.tex.colour(plugin.tag
+ ChatColor.GREEN + "Cleared all data for "
@ -54,11 +54,11 @@ public class Commandpanelsdata implements CommandExecutor {
if (args[1].equalsIgnoreCase("all") || args[1].equalsIgnoreCase("online")) {
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
if (args[1].equalsIgnoreCase("online") && !player.isOnline()) continue;
plugin.panelData.delUserData(plugin.panelData.getOffline(player.getName()), args[2]);
plugin.panelData.delUserData(plugin.panelDataPlayers.getOffline(player.getName()), args[2]);
count++;
}
} else
plugin.panelData.delUserData(plugin.panelData.getOffline(args[1]), args[2]);
plugin.panelData.delUserData(plugin.panelDataPlayers.getOffline(args[1]), args[2]);
if (sendPlayerMessage) {
sender.sendMessage(plugin.tex.colour(plugin.tag
+ ChatColor.GREEN + "Removed "
@ -71,7 +71,7 @@ public class Commandpanelsdata implements CommandExecutor {
//for the get command
sender.sendMessage(plugin.tex.colour(plugin.tag
+ ChatColor.GREEN + "Value of data is "
+ ChatColor.WHITE + plugin.panelData.getUserData(plugin.panelData.getOffline(args[1]), args[2])));
+ ChatColor.WHITE + plugin.panelData.getUserData(plugin.panelDataPlayers.getOffline(args[1]), args[2])));
return true;
}
} else if (args.length == 4) {
@ -80,11 +80,11 @@ public class Commandpanelsdata implements CommandExecutor {
if (args[1].equalsIgnoreCase("all") || args[1].equalsIgnoreCase("online")) {
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
if (args[1].equalsIgnoreCase("online") && !player.isOnline()) continue;
plugin.panelData.setUserData(plugin.panelData.getOffline(player.getName()), args[2], args[3], true);
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(player.getName()), args[2], args[3], true);
count++;
}
} else {
plugin.panelData.setUserData(plugin.panelData.getOffline(args[1]), args[2], args[3], true);
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(args[1]), args[2], args[3], true);
}
if (sendPlayerMessage) {
sender.sendMessage(plugin.tex.colour(plugin.tag
@ -101,11 +101,11 @@ public class Commandpanelsdata implements CommandExecutor {
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
if (args[1].equalsIgnoreCase("online") && !player.isOnline()) continue;
plugin.panelData.setUserData(plugin.panelData.getOffline(player.getName()), args[2], args[3], false);
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(player.getName()), args[2], args[3], false);
count++;
}
} else
plugin.panelData.setUserData(plugin.panelData.getOffline(args[1]), args[2], args[3], false);
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(args[1]), args[2], args[3], false);
if (sendPlayerMessage) {
sender.sendMessage(plugin.tex.colour(plugin.tag
+ ChatColor.GREEN + "Set "

View File

@ -8,11 +8,8 @@ import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
@ -40,6 +37,9 @@ public class Commandpanelsreload implements CommandExecutor {
plugin.config = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder() + File.separator + "config.yml"));
plugin.blockConfig = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder() + File.separator + "blocks.yml"));
//load all known player UUIDs for data
plugin.panelDataPlayers.reloadAllPlayers();
//check for duplicates
plugin.checkDuplicatePanel(sender);
@ -54,9 +54,6 @@ public class Commandpanelsreload implements CommandExecutor {
registerCommands();
}
//pre-cache any player head textures from panels
reloadCachedHeads(sender);
sender.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.reload")));
}else{
sender.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.perms")));
@ -114,47 +111,4 @@ public class Commandpanelsreload implements CommandExecutor {
}
}
//reload player heads
public void reloadCachedHeads(CommandSender sender){
new BukkitRunnable() {
public void run() {
for (Panel temp : plugin.panelList) {
ConfigurationSection yaml = temp.getConfig();
//look through yaml for player heads
if(sender instanceof Player) {
Player player = ((Player) sender).getPlayer();
checkKeysRecursive(temp, player, yaml);
}else{
checkKeysRecursive(temp, null, yaml);
}
}
}
}.runTaskAsynchronously(this.plugin);
}
//this will recursively check through all the keys in a panel for cps= values and find ones with Player names
private void checkKeysRecursive(Panel panel, Player player, ConfigurationSection section) {
for (String key : section.getKeys(false)) {
if (key.equals("material")) {
String value = section.getString(key);
//if value is a custom head
if (value.startsWith("cps=")) {
String[] words = value.split("\\s");
//if value is the length of a players name
if (!words[1].equalsIgnoreCase("self") && words[1].length() <= 16) {
try {
String tempName = plugin.tex.placeholdersNoColour(panel, PanelPosition.Top, player, words[1]);
plugin.customHeads.getPlayerHead(tempName); //get the head cached
}catch (Exception ignore){} //ignore heads that cannot be cached without the panel being open
}
}
}
if (section.isConfigurationSection(key)) {
// Recursive call to check nested sections
checkKeysRecursive(panel, player, section.getConfigurationSection(key));
}
}
}
}

View File

@ -52,11 +52,11 @@ public class CommandRunner {
CommandTagEvent tags = new CommandTagEvent(plugin, panel, position, p, commandRAW);
Bukkit.getPluginManager().callEvent(tags);
if (!tags.commandTagUsed) {
Bukkit.dispatchCommand(p, plugin.tex.placeholders(panel, position, p, commandRAW.trim()));
Bukkit.dispatchCommand(p, plugin.tex.attachPlaceholders(panel, position, p, commandRAW.trim()));
}
}
public void runMultiPaywall(Panel panel, PanelPosition position, Player p, List<String> paywalls, List<String> commands, ClickType click) {
public boolean runMultiPaywall(Panel panel, PanelPosition position, Player p, List<String> paywalls, List<String> commands, ClickType click) {
boolean allPaywallsValid = true;
// New list combining paywalls and commands
@ -79,6 +79,9 @@ public class CommandRunner {
if (allPaywallsValid) {
plugin.commandRunner.runCommands(panel, position, p, allCommands, click);
}
// Return output as boolean for usage if applicable
return allPaywallsValid;
}
//do this on startup to load listeners

View File

@ -3,7 +3,6 @@ package me.rockyhawk.commandpanels.commandtags;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@ -40,8 +39,8 @@ public class CommandTagEvent extends Event {
if(doApiPlaceholders) {
this.args = plugin.tex.attachPlaceholders(panel1,pos, player, split[1].trim()).split("\\s");
}else{
this.args = ChatColor.translateAlternateColorCodes('&',plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),false)).split("\\s");
this.args = ChatColor.translateAlternateColorCodes('&',plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),true)).split("\\s");
this.args = plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),false).split("\\s");
this.args = plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),true).split("\\s");
}
}

View File

@ -31,5 +31,20 @@ public class Hasperm implements Listener {
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
}
if(e.name.equalsIgnoreCase("hasnoperm=")){
//if player uses hasnoperm= [perm]
if (!e.p.hasPermission(e.args[0])) {
if (plugin.config.getBoolean("purchase.permission.enable") && e.doDelete) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.permission.success")).replaceAll("%cp-args%", e.args[0]));
}
e.PAYWALL_OUTPUT = PaywallOutput.Passed;
} else {
if (plugin.config.getBoolean("purchase.currency.enable")) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.permission.failure")));
}
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
}
}
}

View File

@ -1,6 +1,4 @@
package me.rockyhawk.commandpanels.commandtags.paywalls;
import me.realized.tokenmanager.api.TokenManager;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.commandtags.PaywallEvent;
import me.rockyhawk.commandpanels.commandtags.PaywallOutput;
@ -10,45 +8,67 @@ import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.OptionalLong;
public class TokenPaywall implements Listener {
CommandPanels plugin;
private final CommandPanels plugin;
public TokenPaywall(CommandPanels pl) {
this.plugin = pl;
}
@EventHandler
public void commandTag(PaywallEvent e){
if(e.name.equalsIgnoreCase("tokenpaywall=")){
//if player uses tokenpaywall= [price]
public void commandTag(PaywallEvent e) {
if (e.name.equalsIgnoreCase("tokenpaywall=")) {
// if player uses tokenpaywall= [price]
try {
if (plugin.getServer().getPluginManager().isPluginEnabled("TokenManager")) {
final TokenManager api = (TokenManager) Bukkit.getPluginManager().getPlugin("TokenManager");
assert api != null;
int balance = Integer.parseInt(Long.toString(api.getTokens(e.p).orElse(0)));
if (balance >= Double.parseDouble(e.args[0])) {
if (e.doDelete) {
api.removeTokens(e.p, Long.parseLong(e.args[0]));
}
//if the message is empty don't send
if (plugin.config.getBoolean("purchase.tokens.enable") && e.doDelete) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.success")).replaceAll("%cp-args%", e.args[0]));
// Using reflection in this method as TokenManager has issues with Maven due to the Jitpack dependency
Object api = Bukkit.getPluginManager().getPlugin("TokenManager");
if (api != null) {
// Use reflection to access the getTokens and removeTokens methods
Method getTokensMethod = api.getClass().getMethod("getTokens", org.bukkit.entity.Player.class);
Method removeTokensMethod = api.getClass().getMethod("removeTokens", org.bukkit.entity.Player.class, long.class);
// Call getTokens
Object result = getTokensMethod.invoke(api, e.p);
long balance;
if (result instanceof OptionalLong) {
balance = ((OptionalLong) result).orElse(0L);
} else {
balance = 0L;
}
e.PAYWALL_OUTPUT = PaywallOutput.Passed;
} else {
if (plugin.config.getBoolean("purchase.tokens.enable")) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.failure")));
if (balance >= Double.parseDouble(e.args[0])) {
if (e.doDelete) {
// Call removeTokens
removeTokensMethod.invoke(api, e.p, Long.parseLong(e.args[0]));
}
// if the message is empty don't send
if (plugin.config.getBoolean("purchase.tokens.enable") && e.doDelete) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.success")).replaceAll("%cp-args%", e.args[0]));
}
e.PAYWALL_OUTPUT = PaywallOutput.Passed;
} else {
if (plugin.config.getBoolean("purchase.tokens.enable")) {
plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.failure")));
}
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
} else {
plugin.tex.sendString(e.p, plugin.tag + ChatColor.RED + "Needs TokenManager to work!");
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
} else {
plugin.tex.sendString(e.p, plugin.tag + ChatColor.RED + "Needs TokenManager to work!");
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
} catch (Exception buyc) {
plugin.debug(buyc, e.p);
} catch (Exception ex) {
plugin.debug(ex, e.p);
plugin.tex.sendString(e.p, plugin.tag + plugin.config.getString("config.format.error") + " " + "commands: " + e.name);
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}

View File

@ -21,7 +21,7 @@ public class ItemPaywall implements Listener {
@EventHandler
public void commandTag(PaywallEvent e){
if(e.name.equalsIgnoreCase("item-paywall=")){
//if player uses item-paywall= [Material] [Amount] <id:#> <IGNORENBT> WILL NOT TAKE CUSTOM ITEMS. IGNORENBT lets nbt items through. Useful for spawner edge cases.
//if player uses item-paywall= [Material] [Amount] <IGNORENBT> WILL NOT TAKE CUSTOM ITEMS. IGNORENBT lets nbt items through. Useful for spawner edge cases.
//player can use item-paywall= [custom-item] [Amount]
try {
boolean ignoreNBT = false;
@ -46,10 +46,8 @@ public class ItemPaywall implements Listener {
//try to remove the item and determine outcome
PaywallOutput removedItem = PaywallOutput.Blocked;
if (e.doDelete){
if(removeItem(e.p, sellItem, ignoreNBT)){
removedItem = PaywallOutput.Passed;
}
if(removeItem(e.p, sellItem, ignoreNBT, e.doDelete)){
removedItem = PaywallOutput.Passed;
}
//send message and return
@ -73,22 +71,22 @@ public class ItemPaywall implements Listener {
}
}
public boolean removeItem(Player p, ItemStack itemToRemove, boolean ignoreNBT) {
public boolean removeItem(Player p, ItemStack itemToRemove, boolean ignoreNBT, boolean doDelete) {
InventoryOperationResult result;
if (plugin.inventorySaver.hasNormalInventory(p)) {
result = removeItemFromInventory(p.getInventory().getContents(), itemToRemove, ignoreNBT);
result = removeItemFromInventory(p.getInventory().getContents(), itemToRemove, ignoreNBT, doDelete);
p.getInventory().setContents(result.getInventory());
} else {
ItemStack[] savedInventory = plugin.inventorySaver.getNormalInventory(p);
result = removeItemFromInventory(savedInventory, itemToRemove, ignoreNBT);
result = removeItemFromInventory(savedInventory, itemToRemove, ignoreNBT, doDelete);
plugin.inventorySaver.inventoryConfig.set(p.getUniqueId().toString(), plugin.itemSerializer.itemStackArrayToBase64(result.getInventory()));
}
return result.isSuccess(); // Return the success status of the inventory operation
}
private InventoryOperationResult removeItemFromInventory(ItemStack[] inventory, ItemStack itemToRemove, boolean ignoreNBT) {
private InventoryOperationResult removeItemFromInventory(ItemStack[] inventory, ItemStack itemToRemove, boolean ignoreNBT, boolean doDelete) {
int amountToRemove = itemToRemove.getAmount();
int count = 0;
@ -98,10 +96,16 @@ public class ItemPaywall implements Listener {
}
}
//return false if the player doesn't have enough items
if (count < amountToRemove) {
return new InventoryOperationResult(false, inventory); // Not enough items, return with original inventory unchanged
}
//return true because there are enough items, but don't run the code to remove them
if(!doDelete){
return new InventoryOperationResult(true, inventory);
}
for (int i = 0; i < inventory.length; i++) {
ItemStack currentItem = inventory[i];
if (currentItem != null && plugin.itemCreate.isIdentical(currentItem, itemToRemove, !ignoreNBT)) {

View File

@ -16,7 +16,7 @@ public class DataTags implements Listener {
if(e.name.equalsIgnoreCase("set-data=")){
e.commandTagUsed();
if(e.args.length == 3){
plugin.panelData.setUserData(plugin.panelData.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0]),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[1]),true);
return;
@ -30,7 +30,7 @@ public class DataTags implements Listener {
if(e.name.equalsIgnoreCase("add-data=")){
e.commandTagUsed();
if(e.args.length == 3){
plugin.panelData.setUserData(plugin.panelData.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.panelData.setUserData(plugin.panelDataPlayers.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0]),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[1]),false);
return;
@ -44,7 +44,7 @@ public class DataTags implements Listener {
if(e.name.equalsIgnoreCase("math-data=")){
e.commandTagUsed();
if(e.args.length == 3){
plugin.panelData.doDataMath(plugin.panelData.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.panelData.doDataMath(plugin.panelDataPlayers.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[2])),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0]),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[1]));
return;
@ -58,13 +58,13 @@ public class DataTags implements Listener {
if(e.name.equalsIgnoreCase("clear-data=")){
e.commandTagUsed();
//will clear all data for player clear-data= [playerName]
plugin.panelData.clearData(plugin.panelData.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0])));
plugin.panelData.clearData(plugin.panelDataPlayers.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0])));
return;
}
if(e.name.equalsIgnoreCase("del-data=")){
e.commandTagUsed();
if(e.args.length == 2){
plugin.panelData.delUserData(plugin.panelData.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[1])),
plugin.panelData.delUserData(plugin.panelDataPlayers.getOffline(plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[1])),
plugin.tex.placeholdersNoColour(e.panel,e.pos,e.p,e.args[0]));
return;
}

View File

@ -1,8 +1,10 @@
package me.rockyhawk.commandpanels.commandtags.tags.standard;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.PanelCommandEvent;
import me.rockyhawk.commandpanels.classresources.SerializerUtils;
import me.rockyhawk.commandpanels.classresources.MiniMessageUtils;
import me.rockyhawk.commandpanels.commandtags.CommandTagEvent;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelOpenType;
@ -69,6 +71,18 @@ public class BasicTags implements Listener {
e.p.chat("/" + String.join(" ",e.args));
return;
}
if (e.name.equalsIgnoreCase("bungee=")) {
e.commandTagUsed();
ByteArrayDataOutput out = ByteStreams.newDataOutput();
try {
out.writeUTF(e.p.getName());
out.writeUTF(String.join(" ",e.args));
} catch(Exception ex) {
e.p.sendMessage(plugin.tag + ChatColor.translateAlternateColorCodes('&', "&cSomething wrong happened..."));
}
e.p.sendPluginMessage(plugin, "my:psb", out.toByteArray());
return;
}
if(e.name.equalsIgnoreCase("msg=")) {
e.commandTagUsed();
plugin.tex.sendString(e.panel,e.pos,e.p,String.join(" ",e.args));
@ -137,14 +151,10 @@ public class BasicTags implements Listener {
}
if(e.name.equalsIgnoreCase("minimessage=")){
e.commandTagUsed();
//get checks
boolean isVersionCompatible = plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_18);
boolean isPaper = Bukkit.getServer().getVersion().contains("Paper");
boolean allowUnsafeMiniMessage = plugin.config.getBoolean("config.allow-unsafe-mini-message");
//do mini message if conditions are met
if (isVersionCompatible && (isPaper || allowUnsafeMiniMessage)) {
if (plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_18)) {
Audience player = (Audience) e.p; // Needed because the basic Player from the Event can't send Paper's Components
Component parsedText = SerializerUtils.serializeText(String.join(" ", e.args));
Component parsedText = plugin.miniMessage.doMiniMessage(String.join(" ", e.args));
player.sendMessage(parsedText);
} else {
plugin.tex.sendString(e.p, plugin.tag + ChatColor.RED + "MiniMessage-Feature needs Paper 1.18 or newer to work!");

View File

@ -8,8 +8,8 @@ import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Objects;
import java.util.Set;
public class ItemTags implements Listener {
@ -102,21 +102,19 @@ public class ItemTags implements Listener {
e.commandTagUsed();
//if player uses setcustomdata= [slot] [position] [data] it will change the custom model data of the item
PanelPosition position = PanelPosition.valueOf(e.args[1]);
ItemStack EditItem;
ItemStack editItem;
if(position == PanelPosition.Top) {
EditItem = e.p.getOpenInventory().getTopInventory().getItem(Integer.parseInt(e.args[0]));
editItem = e.p.getOpenInventory().getTopInventory().getItem(Integer.parseInt(e.args[0]));
}else if(position == PanelPosition.Middle) {
EditItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])+9);
editItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])+9);
}else{
EditItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0]));
editItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0]));
}
assert EditItem != null;
try{
if(EditItem.hasItemMeta()){
Objects.requireNonNull(EditItem.getItemMeta()).setCustomModelData(Integer.valueOf(e.args[2]));
}
ItemMeta itemMeta = editItem.getItemMeta();
itemMeta.setCustomModelData(Integer.valueOf(e.args[2]));
editItem.setItemMeta(itemMeta);
} catch (Exception err){
plugin.debug(err,e.p);
}

View File

@ -6,7 +6,6 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import java.util.*;
@ -47,13 +46,13 @@ public class DataTabComplete implements TabCompleter {
try {
if (!args[1].equalsIgnoreCase("all") && !args[1].equalsIgnoreCase("online"))
return new ArrayList<>(plugin.panelData.dataConfig.getConfigurationSection("playerData." + plugin.panelData.getOffline(args[1])).getKeys(false));
return new ArrayList<>(plugin.panelData.dataConfig.getConfigurationSection("playerData." + plugin.panelDataPlayers.getOffline(args[1])).getKeys(false));
else {
Set<String> set = new HashSet<>();
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
if (!player.isOnline()&&args[1].equalsIgnoreCase("online")) continue;
set.addAll(plugin.panelData.dataConfig.getConfigurationSection("playerData." + plugin.panelData.getOffline(player.getName())).getKeys(false));
set.addAll(plugin.panelData.dataConfig.getConfigurationSection("playerData." + plugin.panelDataPlayers.getOffline(player.getName())).getKeys(false));
}
String[] finalArgs = args;

View File

@ -1,7 +1,6 @@
package me.rockyhawk.commandpanels.datamanager;
import me.rockyhawk.commandpanels.CommandPanels;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
@ -96,10 +95,4 @@ public class PanelDataLoader {
dataConfig.set("playerData." + playerUUID + "." + dataPoint, output.toPlainString());
}
@SuppressWarnings("deprecation")
public UUID getOffline(String playerName){
//making this a separate function as it is long and deprecated
return Bukkit.getOfflinePlayer(playerName).getUniqueId();
}
}

View File

@ -0,0 +1,40 @@
package me.rockyhawk.commandpanels.datamanager;
import me.rockyhawk.commandpanels.CommandPanels;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.HashMap;
import java.util.UUID;
public class PanelDataPlayerManager implements Listener {
private CommandPanels plugin;
public PanelDataPlayerManager(CommandPanels pl) {
this.plugin = pl;
}
//will return UUID if found or null
public UUID getOffline(String playerName){
return knownPlayers.getOrDefault(playerName, null);
}
//Bukkit.getOfflinePlayer uses MojangAPI and can be very slow if a player has never joined the server before
//Will get all players who have ever joined the server before and use them
private HashMap<String, UUID> knownPlayers = new HashMap<>();
public void reloadAllPlayers(){
knownPlayers.clear();
for(OfflinePlayer p : Bukkit.getOfflinePlayers()){
knownPlayers.put(p.getName(), p.getUniqueId());
}
}
//Add players who have joined the server to known players
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
knownPlayers.put(e.getPlayer().getName(), e.getPlayer().getUniqueId());
}
}

View File

@ -3,19 +3,19 @@ package me.rockyhawk.commandpanels.editor;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import net.md_5.bungee.api.chat.*;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
public class CommandPanelsEditor implements CommandExecutor {
CommandPanels plugin;
@ -81,14 +81,11 @@ public class CommandPanelsEditor implements CommandExecutor {
}
//download the requested panel using an import
if (args.length == 3) {
new BukkitRunnable() {
@Override
public void run() {
downloadPanel(sender,args[1],args[0], args[2]);
plugin.reloadPanelFiles();
plugin.hotbar.reloadHotbarSlots();
}
}.run();
downloadPanel(sender,args[1],args[0], args[2]).thenAccept((ignored) -> {
plugin.reloadPanelFiles();
plugin.hotbar.reloadHotbarSlots();
});
return true;
}
}else{
@ -99,12 +96,17 @@ public class CommandPanelsEditor implements CommandExecutor {
return true;
}
private void downloadPanel(CommandSender sender, String userID, String fileName, String token) {
private CompletableFuture<Void> downloadPanel(CommandSender sender, String userID, String fileName, String token) {
CompletableFuture<Void> future = new CompletableFuture<>();
//get custom editor URL
String url = "https://firebasestorage.googleapis.com/v0/b/commandpanels-website.appspot.com/o/pastes%2F" + userID + "%2F" + fileName + "?alt=media&token=" + token;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.downloader.downloadPanel(sender, url, fileName);
future.complete(null);
});
plugin.downloader.downloadPanel(sender, url, fileName);
return future;
}
private String readFileAsString(String filePath) {

View File

@ -64,8 +64,12 @@ public class OpenFloodgateGUI implements Listener {
form.validResultHandler((SimpleFormResponse response) -> {
int clickedButtonId = response.clickedButtonId();
String configKey = buttonCommands.get(clickedButtonId);
if(fgPanel.contains(configKey + ".commands")) {
plugin.commandRunner.runCommands(e.getPanel(), PanelPosition.Top, e.getPlayer(), fgPanel.getStringList(configKey + ".commands"), null);
String section = plugin.has.hasSection(e.getPanel(), PanelPosition.Top, fgPanel.getConfigurationSection(configKey), e.getPlayer());
ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(configKey + section);
if(buttonConfig.contains("commands")) {
plugin.commandRunner.runCommands(e.getPanel(), PanelPosition.Top, e.getPlayer(), buttonConfig.getStringList("commands"), null);
}
});
@ -77,10 +81,11 @@ public class OpenFloodgateGUI implements Listener {
.filter(key -> key.matches("\\d+"))
.sorted(Comparator.comparingInt(Integer::parseInt)) // Ensure numeric sorting
.map(key -> {
ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(key);
String section = plugin.has.hasSection(panel, PanelPosition.Top, fgPanel.getConfigurationSection(key), p);
ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(key + section);
if (buttonConfig == null) return null;
String buttonContent = plugin.tex.placeholders(panel, null, p, buttonConfig.getString("text"));
String buttonContent = plugin.tex.placeholders(panel, null, p, buttonConfig.getString("text").replaceAll("\\\\n", "\n"));
if (!buttonConfig.contains("icon")) {
form.button(buttonContent);
} else {
@ -101,7 +106,9 @@ public class OpenFloodgateGUI implements Listener {
List<String> commandsOrder = new ArrayList<>();
fgPanel.getKeys(false).forEach(key -> {
if (key.matches("\\d+")) {
ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(key);
String section = plugin.has.hasSection(e.getPanel(), e.getPosition(), fgPanel.getConfigurationSection(key), e.getPlayer());
ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(key + section);
try {
String type = "toggle";
if(fieldConfig.contains("type")) {
@ -109,12 +116,12 @@ public class OpenFloodgateGUI implements Listener {
}
switch (type) {
case "toggle":
form.toggle(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text")),
form.toggle(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text").replaceAll("\\\\n", "\n")),
Boolean.parseBoolean(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("default"))));
commandsOrder.add(key);
break;
case "slider":
form.slider(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text")),
form.slider(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text").replaceAll("\\\\n", "\n")),
Long.parseLong(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("min"))),
Long.parseLong(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("max"))),
Long.parseLong(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("step"))),
@ -122,13 +129,13 @@ public class OpenFloodgateGUI implements Listener {
commandsOrder.add(key);
break;
case "input":
form.input(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text")),
form.input(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text").replaceAll("\\\\n", "\n")),
plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("placeholder")),
plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("default")));
commandsOrder.add(key);
break;
case "dropdown":
form.dropdown(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text")),
form.dropdown(plugin.tex.placeholders(e.getPanel(), null, e.getPlayer(), fieldConfig.getString("text").replaceAll("\\\\n", "\n")),
plugin.tex.placeholdersList(e.getPanel(), null, e.getPlayer(), fieldConfig.getStringList("options"), true));
commandsOrder.add(key);
break;
@ -145,10 +152,13 @@ public class OpenFloodgateGUI implements Listener {
if (!response.hasNext()) {
break; // Safety check to prevent NoSuchElementException
}
if(fgPanel.contains(configKey + ".commands")) {
String section = plugin.has.hasSection(e.getPanel(), e.getPosition(), fgPanel.getConfigurationSection(configKey), e.getPlayer());
ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(configKey + section);
if(fieldConfig.contains("commands")) {
Object fieldValue = response.next(); // Retrieve the next response value
String value = String.valueOf(fieldValue); // Convert the field value to String
List<String> commands = fgPanel.getStringList(configKey + ".commands"); // Retrieve commands for this field
List<String> commands = fieldConfig.getStringList("commands"); // Retrieve commands for this field
List<String> processedCommands = new ArrayList<>();
for (String command : commands) {
processedCommands.add(command.replaceAll("%cp-input%", value)); // Replace the placeholder in each command

View File

@ -22,6 +22,10 @@ public class Commandpanelrefresher implements Listener {
}
@EventHandler
public void onPanelOpen(PanelOpenedEvent e){ //Handles when Players open inventory
if(e.isCancelled()){
return;
}
if (plugin.config.contains("config.refresh-panels")) {
if (Objects.requireNonNull(plugin.config.getString("config.refresh-panels")).trim().equalsIgnoreCase("false")) {
return;

View File

@ -1,6 +1,6 @@
package me.rockyhawk.commandpanels.ioclasses.nbt;
import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
@ -10,8 +10,6 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
public class NBTManager {
CommandPanels plugin;
public NBTManager(CommandPanels pl) {
@ -25,13 +23,29 @@ public class NBTManager {
return nbtitem1.equals(nbtitem2);
}
public ItemStack setNBT(ItemStack item, String key, String value) {
if (item != null) {
NBT.modify(item, nbt -> {
nbt.setString(key, value);
});
public Object getNBT(ItemStack item, String key, String type) {
type = type.toLowerCase();
NBTItem nbtItem = new NBTItem(item);
switch(type.toLowerCase()) {
case "byte":
return nbtItem.getByte(key);
case "boolean":
return nbtItem.getBoolean(key);
case "short":
return nbtItem.getShort(key);
case "integer":
return nbtItem.getInteger(key);
case "long":
return nbtItem.getLong(key);
case "float":
return nbtItem.getFloat(key);
case "double":
return nbtItem.getDouble(key);
case "string":
return nbtItem.getString(key);
default:
throw new IllegalArgumentException("Unsupported NBT type: " + type);
}
return item;
}
public boolean hasNBT(ItemStack item, String key){
@ -39,23 +53,127 @@ public class NBTManager {
return nbti.hasTag(key);
}
public String getNBT(ItemStack item, String key){
NBTItem nbti = new NBTItem(item);
if(!nbti.hasNBTData()) return "";
return nbti.getString(key);
//nbt will be assigned with the value "string_value_1
//which uses the type "String" and value "value_1"
// Method to apply NBT recursively
public ItemStack setNBT(ItemStack item, String key, String value) {
if (item == null || item.getType() == Material.AIR) {
return item;
}
NBTItem nbtItem = new NBTItem(item);
setNBTDirectlyOnItem(nbtItem, key, value);
item.setItemMeta(nbtItem.getItem().getItemMeta());
return item;
}
public void applyNBTRecursively(String path, ConfigurationSection section, ItemStack s, Player p, Panel panel, PanelPosition position) {
for (String key : section.getKeys(true)) {
String fullPath = path.isEmpty() ? key : path + "." + key;
// Method to apply NBT recursively
public void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
NBTItem nbtItem = new NBTItem(item);
// Iterate over each key in the root of the ConfigurationSection
for (String key : section.getKeys(false)) {
// Check if the key represents a ConfigurationSection
if (section.isConfigurationSection(key)) {
// Recursive call for nested sections
applyNBTRecursively(fullPath, section.getConfigurationSection(key), s, p, panel, position);
// Create a compound for each ConfigurationSection
NBTCompound compound = nbtItem.addCompound(key);
convertSectionToNBT(compound, section.getConfigurationSection(key), player, panel, position);
} else {
// Set the NBT tag at this level
String value = plugin.tex.attachPlaceholders(panel, position, p, section.getString(key));
s = plugin.nbt.setNBT(s, fullPath, value);
// Handle non-section values directly on the NBTItem if necessary
String value = plugin.tex.attachPlaceholders(panel, position, player, section.getString(key));
setNBTDirectlyOnItem(nbtItem, key, value);
}
}
item.setItemMeta(nbtItem.getItem().getItemMeta());
}
// Convert ConfigurationSection to NBTCompound recursively
private void convertSectionToNBT(NBTCompound compound, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
for (String key : section.getKeys(false)) {
if (section.isConfigurationSection(key)) {
// Recursively convert sub-sections to their own compounds
NBTCompound subCompound = compound.addCompound(key);
convertSectionToNBT(subCompound, section.getConfigurationSection(key), player, panel, position);
} else {
// Handle scalar values within each compound
String value = plugin.tex.attachPlaceholders(panel, position, player, section.getString(key));
setNBTOnCompound(compound, key, value);
}
}
}
private void setNBTDirectlyOnItem(NBTItem nbtItem, String key, String value) {
int underscoreIndex = value.indexOf("_");
if (underscoreIndex == -1) return; // Skip if format is invalid
String type = value.substring(0, underscoreIndex);
String val = value.substring(underscoreIndex + 1);
switch (type.toLowerCase()) {
case "byte":
nbtItem.setByte(key, Byte.parseByte(val));
break;
case "boolean":
nbtItem.setBoolean(key, Boolean.parseBoolean(val));
break;
case "short":
nbtItem.setShort(key, Short.parseShort(val));
break;
case "integer":
nbtItem.setInteger(key, Integer.parseInt(val));
break;
case "long":
nbtItem.setLong(key, Long.parseLong(val));
break;
case "float":
nbtItem.setFloat(key, Float.parseFloat(val));
break;
case "double":
nbtItem.setDouble(key, Double.parseDouble(val));
break;
case "string":
nbtItem.setString(key, val);
break;
default:
throw new IllegalArgumentException("Unsupported NBT type: " + type);
}
}
// Set typed value on an NBTCompound
private void setNBTOnCompound(NBTCompound compound, String key, String value) {
int underscoreIndex = value.indexOf("_");
if (underscoreIndex == -1) return; // Invalid format, skip
String type = value.substring(0, underscoreIndex);
String val = value.substring(underscoreIndex + 1);
switch (type.toLowerCase()) {
case "byte":
compound.setByte(key, Byte.parseByte(val));
break;
case "boolean":
compound.setBoolean(key, Boolean.parseBoolean(val));
break;
case "short":
compound.setShort(key, Short.parseShort(val));
break;
case "integer":
compound.setInteger(key, Integer.parseInt(val));
break;
case "long":
compound.setLong(key, Long.parseLong(val));
break;
case "float":
compound.setFloat(key, Float.parseFloat(val));
break;
case "double":
compound.setDouble(key, Double.parseDouble(val));
break;
case "string":
compound.setString(key, val);
break;
default:
throw new IllegalArgumentException("Unsupported NBT type: " + type);
}
}
}

View File

@ -0,0 +1,77 @@
package me.rockyhawk.commandpanels.ioclasses.potions;
import me.rockyhawk.commandpanels.CommandPanels;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ClassicPotionData {
private CommandPanels plugin;
public ClassicPotionData(CommandPanels plugin) {
this.plugin = plugin;
}
public void applyPotionEffect(Player p, ItemStack item, String[] effectType) {
try {
if (item == null || item.getType() != org.bukkit.Material.POTION) {
p.sendMessage(plugin.tex.colour(plugin.tag + ChatColor.RED + "Item is not a potion."));
return;
}
Class<?> potionClass = Class.forName("org.bukkit.potion.Potion");
Class<?> potionTypeClass = Class.forName("org.bukkit.potion.PotionType");
Object potionType = Enum.valueOf((Class<Enum>) potionTypeClass, effectType[0].toUpperCase());
Constructor<?> potionConstructor = potionClass.getConstructor(potionTypeClass, int.class);
Object potion = potionConstructor.newInstance(potionType, effectType.length >= 3 && effectType[2].equalsIgnoreCase("true") ? 2 : 1);
Method setSplashMethod = potionClass.getMethod("setSplash", boolean.class);
boolean isSplash = (item.getDurability() & 0x4000) != 0; // Checks if the durability indicates it's a splash potion
setSplashMethod.invoke(potion, isSplash);
try {
Method setHasExtendedDurationMethod = potionClass.getMethod("setHasExtendedDuration", boolean.class);
setHasExtendedDurationMethod.invoke(potion, effectType.length >= 2 && effectType[1].equalsIgnoreCase("true"));
}catch (Exception ignore){
//ignore as some potions like instant potions cannot be extended
}
Method applyMethod = potionClass.getMethod("apply", ItemStack.class);
applyMethod.invoke(potion, item);
} catch (Exception er) {
plugin.debug(er, p);
p.sendMessage(plugin.tex.colour(plugin.tag + ChatColor.RED + "Incorrect potion type or format."));
}
}
public String retrievePotionData(ItemStack item) {
try {
if (item == null || item.getType() != org.bukkit.Material.POTION) {
return "Item is not a potion";
}
Class<?> potionClass = Class.forName("org.bukkit.potion.Potion");
Method fromItemStackMethod = potionClass.getMethod("fromItemStack", ItemStack.class);
Object potion = fromItemStackMethod.invoke(null, item);
Method getTypeMethod = potionClass.getMethod("getType");
Method isExtendedMethod = potionClass.getMethod("hasExtendedDuration");
Method isUpgradedMethod = potionClass.getMethod("getLevel");
Object potionType = getTypeMethod.invoke(potion);
boolean extended = (Boolean) isExtendedMethod.invoke(potion);
boolean upgraded = ((Integer) isUpgradedMethod.invoke(potion)) > 1;
boolean isSplash = (item.getDurability() & 0x4000) != 0; // Checks if the durability indicates it's a splash potion
return potionType.toString() + " " + extended + " " + upgraded + " Splash:" + isSplash;
} catch (Exception e) {
plugin.debug(e, null);
return "Failed to retrieve potion data";
}
}
}

View File

@ -10,6 +10,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class LegacyPotionData {
/*
* To be used in Minecraft 1.9 to 1.20.4
* */
private CommandPanels plugin;
public LegacyPotionData(CommandPanels plugin) {

View File

@ -9,7 +9,6 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@ -50,9 +49,9 @@ public class OpenGUI {
Set<String> itemList = pconfig.getConfigurationSection("item").getKeys(false);
HashSet<Integer> takenSlots = new HashSet<>();
for (String item : itemList) {
String section = "";
String section;
//openType needs to not be 3 so the editor won't include hasperm and hasvalue, etc items
section = plugin.has.hasSection(panel,position,pconfig.getConfigurationSection("item." + Integer.parseInt(item)), p);
section = plugin.has.hasSection(panel,position,pconfig.getConfigurationSection("item." + item), p);
//This section is for animations below here: VISUAL ONLY
//check for if there is animations inside the items section
@ -70,17 +69,16 @@ public class OpenGUI {
//this is for contents in the itemType section
if (pconfig.getStringList("item." + item + section + ".itemType").contains("placeable") && openType == PanelOpenType.Refresh) {
//keep item the same, openType == 0 meaning panel is refreshing
setItem(p.getOpenInventory().getItem(Integer.parseInt(item)),Integer.parseInt(item),i,p,position);
takenSlots.add(Integer.parseInt(item));
setItem(p.getOpenInventory().getItem(Integer.parseInt(plugin.tex.placeholdersNoColour(panel,position,p,item))), item, i, p, position);
takenSlots.add(Integer.parseInt(plugin.tex.placeholdersNoColour(panel,position,p,item)));
continue;
}
}
try {
//place item into the GUI
setItem(s,Integer.parseInt(item),i,p,position);
takenSlots.add(Integer.parseInt(item));
//i.setItem(Integer.parseInt(item), s);
setItem(s,item,i,p,position);
takenSlots.add(Integer.parseInt(plugin.tex.placeholdersNoColour(panel,position,p,item)));
//only place duplicate items in without the editor mode. These are merely visual and will not carry over commands
if(pconfig.contains("item." + item + section + ".duplicate")) {
try {
@ -121,19 +119,20 @@ public class OpenGUI {
}
} catch (ArrayIndexOutOfBoundsException ignore) {}
}
if (pconfig.contains("empty") && !Objects.equals(pconfig.getString("empty"), "AIR")) {
if (pconfig.contains("empty") && !plugin.tex.placeholdersNoColour(panel,position,p,pconfig.getString("empty")).equals("AIR")) {
String emptyValue = plugin.tex.placeholdersNoColour(panel,position,p,pconfig.getString("empty"));
ItemStack empty;
try {
//emptyID for older versions of minecraft (might be deprecated later on)
short id = 0;
if(pconfig.contains("emptyID")){
id = Short.parseShort(pconfig.getString("emptyID"));
id = Short.parseShort(plugin.tex.placeholdersNoColour(panel,position,p,pconfig.getString("emptyID")));
}
//either use custom item or just material type
if(pconfig.contains("custom-item." + pconfig.getString("empty"))){
empty = plugin.itemCreate.makeItemFromConfig(panel,position,pconfig.getConfigurationSection("custom-item." + pconfig.getString("empty")),p,true,true,true);
if(pconfig.contains("custom-item." + emptyValue)){
empty = plugin.itemCreate.makeItemFromConfig(panel,position,pconfig.getConfigurationSection("custom-item." + emptyValue),p,true,true,true);
}else{
empty = new ItemStack(Objects.requireNonNull(Material.matchMaterial(pconfig.getString("empty").toUpperCase())), 1,id);
empty = new ItemStack(Objects.requireNonNull(Material.matchMaterial(emptyValue.toUpperCase())), 1,id);
ItemMeta renamedMeta = empty.getItemMeta();
assert renamedMeta != null;
renamedMeta.setDisplayName(" ");
@ -192,6 +191,12 @@ public class OpenGUI {
return 9;
}
}
//allows for a string to be used instead of integer, for placeholders
private void setItem(ItemStack item, String slotName, Inventory inv, Player p, PanelPosition position) throws ArrayIndexOutOfBoundsException{
int slot = Integer.parseInt(plugin.tex.placeholdersNoColour(null, position, p, slotName));
setItem(item, slot, inv, p, position);
}
private void setItem(ItemStack item, int slot, Inventory inv, Player p, PanelPosition position) throws ArrayIndexOutOfBoundsException{
if(position == PanelPosition.Top){
inv.setItem(slot, item);

View File

@ -5,6 +5,7 @@ import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.api.PanelClosedEvent;
import me.rockyhawk.commandpanels.api.PanelsInterface;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.*;
@ -130,7 +131,18 @@ public class OpenPanelsLoader {
}
}
public boolean isNBTInjected(ItemStack itm){
//if any CommandPanels items exist outside a panel, they will be removed
public void deleteCommandPanelsItems(Player p){
for(ItemStack itm : p.getInventory().getContents()){
if(itm != null){
if (plugin.nbt.hasNBT(itm, "CommandPanelsItem")) {
p.getInventory().remove(itm);
}
}
}
}
//checks if an item is from cpanel or not
public boolean isCommandPanelsItem(ItemStack itm){
if(itm != null){
return plugin.nbt.hasNBT(itm,"CommandPanelsItem");
}

View File

@ -4,6 +4,7 @@ import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.api.PanelClosedEvent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -12,10 +13,7 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class UtilsPanelsLoader implements Listener {
@ -30,13 +28,7 @@ public class UtilsPanelsLoader implements Listener {
plugin.openPanels.closePanelForLoader(e.getPlayer().getName(),PanelPosition.Top);
Player p = e.getPlayer();
p.updateInventory();
for(ItemStack itm : p.getInventory().getContents()){
if(itm != null){
if (plugin.nbt.hasNBT(itm, "CommandPanelsItem")) {
p.getInventory().remove(itm);
}
}
}
plugin.openPanels.deleteCommandPanelsItems(p);
}
//tell panel loader that player has closed the panel (there is also one of these in EditorUtils)
@ -79,12 +71,6 @@ public class UtilsPanelsLoader implements Listener {
//close panels and run commands for Top panel
plugin.openPanels.closePanelForLoader(e.getPlayer().getName(),PanelPosition.Top);
//clear cached textures list until length limit is reached
while (plugin.customHeads.playerHeadTextures.size() > 2000) {
List<String> keys = new ArrayList<>(plugin.customHeads.playerHeadTextures.keySet());
plugin.customHeads.playerHeadTextures.remove(keys.get(0));
}
}
@EventHandler
@ -92,10 +78,10 @@ public class UtilsPanelsLoader implements Listener {
//this will check to ensure an item is not from CommandPanels on inventory open
Player p = (Player)e.getWhoClicked();
if(!plugin.openPanels.hasPanelOpen(p.getName(),PanelPosition.Top)){
for(ItemStack itm : p.getInventory().getContents()){
if(plugin.openPanels.isNBTInjected(itm)){
p.getInventory().remove(itm);
}
if(e.getCurrentItem() == null){return;}
if(e.getCurrentItem().getType() == Material.AIR){return;}
if(plugin.openPanels.isCommandPanelsItem(e.getCurrentItem())){
p.getInventory().remove(e.getCurrentItem());
}
}
}

View File

@ -36,7 +36,7 @@ public class HotbarItemLoader {
if(stationaryItems.get(p.getUniqueId()).list.containsKey(String.valueOf(slot))){
if(openPanel) {
try {
if (!plugin.nbt.getNBT(p.getInventory().getItem(slot), "CommandPanelsHotbar").split(":")[1].equals(String.valueOf(slot))) {
if (!String.valueOf(plugin.nbt.getNBT(p.getInventory().getItem(slot), "CommandPanelsHotbar", "string")).split(":")[1].equals(String.valueOf(slot))) {
return false;
}
}catch(Exception ex){
@ -64,7 +64,7 @@ public class HotbarItemLoader {
//return true if found
public boolean itemCheckExecute(ItemStack invItem, Player p, boolean openPanel, boolean stationaryOnly){
try {
if (Objects.equals(plugin.nbt.getNBT(invItem, "CommandPanelsHotbar"), "")) {
if (Objects.equals(String.valueOf(plugin.nbt.getNBT(invItem, "CommandPanelsHotbar", "string")), "")) {
return false;
}
}catch(IllegalArgumentException | NullPointerException nu){
@ -73,13 +73,13 @@ public class HotbarItemLoader {
for(Panel panel : plugin.panelList) {
if(stationaryOnly){
try {
if (plugin.nbt.getNBT(invItem, "CommandPanelsHotbar").split(":")[1].equals("-1")) {
if (String.valueOf(plugin.nbt.getNBT(invItem, "CommandPanelsHotbar", "string")).split(":")[1].equals("-1")) {
continue;
}
}catch(NullPointerException | IllegalArgumentException ignore){}
}
if(panel.hasHotbarItem()){
if(plugin.nbt.getNBT(invItem,"CommandPanelsHotbar").split(":")[0].equals(panel.getName())){
if(String.valueOf(plugin.nbt.getNBT(invItem, "CommandPanelsHotbar", "string")).split(":")[0].equals(panel.getName())){
if(openPanel) {
//only open panel automatically if there are no commands and if world is not disabled
if(!plugin.panelPerms.isPanelWorldEnabled(p,panel.getConfig())){
@ -120,9 +120,9 @@ public class HotbarItemLoader {
stationaryItems.put(p.getUniqueId(),new HotbarPlayerManager());
for(int i = 0; i <= 35; i++){
try {
if (!Objects.equals(plugin.nbt.getNBT(p.getInventory().getItem(i), "CommandPanelsHotbar"), "")) {
if (!Objects.equals(String.valueOf(plugin.nbt.getNBT(p.getInventory().getItem(i), "CommandPanelsHotbar", "string")), "")) {
//do not remove items that are not stationary
if(!plugin.nbt.getNBT(p.getInventory().getItem(i), "CommandPanelsHotbar").endsWith("-1")) {
if(!String.valueOf(plugin.nbt.getNBT(p.getInventory().getItem(i), "CommandPanelsHotbar","string")).endsWith("-1")) {
p.getInventory().setItem(i, new ItemStack(Material.AIR));
}
}

View File

@ -110,9 +110,9 @@ public class UtilsOpenWithItem implements Listener {
try {
for (ItemStack s : new ArrayList<>(e.getDrops())) {
try {
if (!plugin.nbt.getNBT(s, "CommandPanelsHotbar").isEmpty()) {
if (!String.valueOf(plugin.nbt.getNBT(s, "CommandPanelsHotbar", "string")).isEmpty()) {
//do not remove items that are not stationary
if (!plugin.nbt.getNBT(s, "CommandPanelsHotbar").endsWith("-1")) {
if (!String.valueOf(plugin.nbt.getNBT(s, "CommandPanelsHotbar", "string")).endsWith("-1")) {
e.getDrops().remove(s);
}
}