NBT fallback feature and bug fixes

This commit is contained in:
rockyhawk64 2025-04-05 17:20:43 +11:00
parent 8aecaacb6d
commit 5be00ca85e
8 changed files with 356 additions and 148 deletions

View File

@ -44,6 +44,8 @@ public class Commandpanelslist implements CommandExecutor {
}
sender.sendMessage(plugin.tex.colour(plugin.tag + ChatColor.DARK_AQUA + "Panels: (Page " + page + ")"));
for (int f = skip; panels.size() > f && skip+8 > f; f++) {
if(panels.get(f).getFile() == null){ continue; }
sender.sendMessage(ChatColor.DARK_GREEN + panels.get(f).getFile().getAbsolutePath().replace(plugin.panelsf.getAbsolutePath(),"") + ChatColor.GREEN + " " + panels.get(f).getName());
if(panels.size()-1 == f){
return true;

View File

@ -19,6 +19,7 @@ public class ImportTabComplete implements TabCompleter {
ArrayList<String> output = new ArrayList<>();
if(args.length == 1){
for(Panel panel : plugin.panelList){
if(panel.getFile() == null){ continue; }
output.add(panel.getFile().getName());
}
}

View File

@ -40,6 +40,7 @@ public class CommandPanelsEditor implements CommandExecutor {
//export the requested panel
if (args.length == 1) {
for (Panel panel : plugin.panelList) {
if(panel.getFile() == null){ continue; }
if (panel.getFile().getName().equals(args[0])) {
String filePath = panel.getFile().getAbsolutePath(); //remove file name extensions
String fileContents = readFileAsString(filePath);

View File

@ -19,6 +19,7 @@ public class EditorTabComplete implements TabCompleter {
ArrayList<String> output = new ArrayList<>();
if(args.length == 1){
for(Panel panel : plugin.panelList){
if(panel.getFile() == null){ continue; }
output.add(panel.getFile().getName());
}
}

View File

@ -0,0 +1,173 @@
package me.rockyhawk.commandpanels.ioclasses.nbt;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NBTType;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.LinkedHashMap;
import java.util.Map;
public class NBTAPIHandler implements NBTHandler{
public boolean hasSameNBT(ItemStack one, ItemStack two) {
return new NBTItem(one).equals(new NBTItem(two));
}
public boolean hasNBT(ItemStack item, String key) {
return new NBTItem(item).hasTag(key);
}
public ItemStack setNBT(ItemStack item, String key, Object value) {
if (item == null || item.getType() == Material.AIR) return item;
NBTItem nbtItem = new NBTItem(item);
setNBTValue(nbtItem, key, value);
item.setItemMeta(nbtItem.getItem().getItemMeta());
return item;
}
public void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
if (item == null || item.getType() == Material.AIR) return;
NBTItem nbtItem = new NBTItem(item);
for (String key : section.getKeys(false)) {
Object value = section.get(key);
if (section.isConfigurationSection(key)) {
NBTCompound compound = nbtItem.addCompound(key);
convertSectionToNBT(compound, section.getConfigurationSection(key), player, panel, position);
} else {
setNBTValue(nbtItem, key, value);
}
}
item.setItemMeta(nbtItem.getItem().getItemMeta());
}
public void saveMapToYAML(Map<String, Object> map, ConfigurationSection section) {
if (map == null || section == null) return;
map.forEach((key, value) -> {
if (value instanceof Map) {
saveMapToYAML((Map<String, Object>) value, section.createSection(key));
} else if (value instanceof Byte) {
// Convert only Byte values that are actually Boolean representations
byte byteValue = (Byte) value;
if (byteValue == 1 || byteValue == 0) {
section.set(key, byteValue == 1);
} else {
section.set(key, byteValue);
}
} else {
section.set(key, value);
}
});
}
private void convertSectionToNBT(NBTCompound compound, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
for (String key : section.getKeys(false)) {
Object value = section.get(key);
if (section.isConfigurationSection(key)) {
// Properly handles nested compounds instead of treating them as strings
convertSectionToNBT(compound.addCompound(key), section.getConfigurationSection(key), player, panel, position);
} else {
setNBTValue(compound, key, value);
}
}
}
private void setNBTValue(NBTCompound compound, String key, Object value) {
if (value instanceof Boolean) {
compound.setByte(key, (Boolean) value ? (byte) 1 : (byte) 0); // Store as NBT Byte since boolean is shown as 1b or 0b
} else if (value instanceof Integer) {
compound.setInteger(key, (Integer) value);
} else if (value instanceof Double) {
compound.setDouble(key, (Double) value);
} else if (value instanceof Long) {
compound.setLong(key, (Long) value);
} else if (value instanceof Short) {
compound.setShort(key, (Short) value);
} else if (value instanceof Float) {
compound.setFloat(key, (Float) value);
} else if (value instanceof Byte) {
compound.setByte(key, (Byte) value);
} else if (value instanceof Map) {
saveMapToNBTCompound((Map<String, Object>) value, compound.addCompound(key));
} else {
compound.setString(key, value.toString());
}
}
public Object getNBTValue(ItemStack item, String key) {
if (item == null || item.getType() == Material.AIR) return null;
NBTItem nbtItem = new NBTItem(item);
if (!nbtItem.hasTag(key)) return null;
return extractNBTValue(nbtItem, key, nbtItem.getType(key));
}
private Object extractNBTValue(NBTCompound compound, String key, NBTType type) {
switch (type) {
case NBTTagInt:
return compound.getInteger(key);
case NBTTagDouble:
return compound.getDouble(key);
case NBTTagByte:
byte byteValue = compound.getByte(key);
if (byteValue == 1 || byteValue == 0) {
return byteValue == 1; // Convert to Boolean since its displayed as 1b and 0b
}
return byteValue;
case NBTTagFloat:
return compound.getFloat(key);
case NBTTagLong:
return compound.getLong(key);
case NBTTagShort:
return compound.getShort(key);
case NBTTagByteArray:
return compound.getByteArray(key);
case NBTTagIntArray:
return compound.getIntArray(key);
case NBTTagList:
return compound.getStringList(key); // Assuming it's a String list
case NBTTagCompound:
return convertCompoundToMap(compound.getCompound(key)); // Recursively convert sub-compounds
case NBTTagString:
String str = compound.getString(key);
if (str.equalsIgnoreCase("true")) return true;
if (str.equalsIgnoreCase("false")) return false;
if (str.matches("-?\\d+")) return Integer.parseInt(str);
if (str.matches("-?\\d+\\.\\d+")) return Double.parseDouble(str);
return str;
default:
return null;
}
}
private Map<String, Object> convertCompoundToMap(NBTCompound compound) {
Map<String, Object> compoundMap = new LinkedHashMap<>();
compound.getKeys().forEach(key -> compoundMap.put(key, extractNBTValue(compound, key, compound.getType(key))));
return compoundMap;
}
private void saveMapToNBTCompound(Map<String, Object> map, NBTCompound compound) {
map.forEach((key, value) -> {
if (value instanceof Map) {
saveMapToNBTCompound((Map<String, Object>) value, compound.addCompound(key));
} else {
setNBTValue(compound, key, value);
}
});
}
}

View File

@ -0,0 +1,18 @@
package me.rockyhawk.commandpanels.ioclasses.nbt;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
public interface NBTHandler {
boolean hasSameNBT(ItemStack one, ItemStack two);
boolean hasNBT(ItemStack item, String key);
ItemStack setNBT(ItemStack item, String key, Object value);
Object getNBTValue(ItemStack item, String key);
void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position);
void saveMapToYAML(Map<String, Object> map, ConfigurationSection section);
}

View File

@ -1,179 +1,74 @@
package me.rockyhawk.commandpanels.ioclasses.nbt;
import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NBTType;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.LinkedHashMap;
import java.util.Map;
public class NBTManager {
CommandPanels plugin;
/*
NBT will use NBTAPI, and if that doesn't work in the MC version necessary will instead use PersistentData
public NBTManager(CommandPanels pl) {
this.plugin = pl;
NBTManager.java <-- Main entry point, handles logic and fallback
NBTHandler.java <-- Interface
NBTAPIHandler.java <-- Primary handler using NBTAPI for NBT
PersistentDataHandler.java <-- Fallback using PersistentDataContainer
*/
public class NBTManager {
private final NBTHandler handler;
public NBTManager(CommandPanels plugin) {
NBTHandler nbtHandler;
try {
// Try using NBTAPIHandler
nbtHandler = new NBTAPIHandler();
ItemStack test = new ItemStack(Material.STONE);
String testKey = "nbt_test_key";
String testValue = "hello";
ItemStack modified = nbtHandler.setNBT(test, testKey, testValue);
Object result = nbtHandler.getNBTValue(modified, testKey);
//Should never happen, would indicate an issue with NBTAPI
if (result == null || !result.equals(testValue)) {
throw new IllegalStateException("NBTAPI test failed values didn't match.");
}
} catch (Throwable ex) {
plugin.getServer().getConsoleSender().sendMessage(ChatColor.RED + "[CommandPanels] NBTAPI Error: version being used may not be compatible with this version of Minecraft. Falling back to use PersistentData.");
nbtHandler = new PersistentDataHandler(plugin);
}
this.handler = nbtHandler;
}
public boolean hasSameNBT(ItemStack one, ItemStack two) {
return new NBTItem(one).equals(new NBTItem(two));
return handler.hasSameNBT(one, two);
}
public boolean hasNBT(ItemStack item, String key) {
return new NBTItem(item).hasTag(key);
return handler.hasNBT(item, key);
}
public ItemStack setNBT(ItemStack item, String key, Object value) {
if (item == null || item.getType() == Material.AIR) return item;
NBTItem nbtItem = new NBTItem(item);
setNBTValue(nbtItem, key, value);
item.setItemMeta(nbtItem.getItem().getItemMeta());
return item;
}
public void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
if (item == null || item.getType() == Material.AIR) return;
NBTItem nbtItem = new NBTItem(item);
for (String key : section.getKeys(false)) {
Object value = section.get(key);
if (section.isConfigurationSection(key)) {
NBTCompound compound = nbtItem.addCompound(key);
convertSectionToNBT(compound, section.getConfigurationSection(key), player, panel, position);
} else {
setNBTValue(nbtItem, key, value);
}
}
item.setItemMeta(nbtItem.getItem().getItemMeta());
}
public void saveMapToYAML(Map<String, Object> map, ConfigurationSection section) {
if (map == null || section == null) return;
map.forEach((key, value) -> {
if (value instanceof Map) {
saveMapToYAML((Map<String, Object>) value, section.createSection(key));
} else if (value instanceof Byte) {
// Convert only Byte values that are actually Boolean representations
byte byteValue = (Byte) value;
if (byteValue == 1 || byteValue == 0) {
section.set(key, byteValue == 1);
} else {
section.set(key, byteValue);
}
} else {
section.set(key, value);
}
});
}
private void convertSectionToNBT(NBTCompound compound, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
for (String key : section.getKeys(false)) {
Object value = section.get(key);
if (section.isConfigurationSection(key)) {
// Properly handles nested compounds instead of treating them as strings
convertSectionToNBT(compound.addCompound(key), section.getConfigurationSection(key), player, panel, position);
} else {
setNBTValue(compound, key, value);
}
}
}
private void setNBTValue(NBTCompound compound, String key, Object value) {
if (value instanceof Boolean) {
compound.setByte(key, (Boolean) value ? (byte) 1 : (byte) 0); // Store as NBT Byte since boolean is shown as 1b or 0b
} else if (value instanceof Integer) {
compound.setInteger(key, (Integer) value);
} else if (value instanceof Double) {
compound.setDouble(key, (Double) value);
} else if (value instanceof Long) {
compound.setLong(key, (Long) value);
} else if (value instanceof Short) {
compound.setShort(key, (Short) value);
} else if (value instanceof Float) {
compound.setFloat(key, (Float) value);
} else if (value instanceof Byte) {
compound.setByte(key, (Byte) value);
} else if (value instanceof Map) {
saveMapToNBTCompound((Map<String, Object>) value, compound.addCompound(key));
} else {
compound.setString(key, value.toString());
}
return handler.setNBT(item, key, value);
}
public Object getNBTValue(ItemStack item, String key) {
if (item == null || item.getType() == Material.AIR) return null;
NBTItem nbtItem = new NBTItem(item);
if (!nbtItem.hasTag(key)) return null;
return extractNBTValue(nbtItem, key, nbtItem.getType(key));
return handler.getNBTValue(item, key);
}
private Object extractNBTValue(NBTCompound compound, String key, NBTType type) {
switch (type) {
case NBTTagInt:
return compound.getInteger(key);
case NBTTagDouble:
return compound.getDouble(key);
case NBTTagByte:
byte byteValue = compound.getByte(key);
if (byteValue == 1 || byteValue == 0) {
return byteValue == 1; // Convert to Boolean since its displayed as 1b and 0b
}
return byteValue;
case NBTTagFloat:
return compound.getFloat(key);
case NBTTagLong:
return compound.getLong(key);
case NBTTagShort:
return compound.getShort(key);
case NBTTagByteArray:
return compound.getByteArray(key);
case NBTTagIntArray:
return compound.getIntArray(key);
case NBTTagList:
return compound.getStringList(key); // Assuming it's a String list
case NBTTagCompound:
return convertCompoundToMap(compound.getCompound(key)); // Recursively convert sub-compounds
case NBTTagString:
String str = compound.getString(key);
if (str.equalsIgnoreCase("true")) return true;
if (str.equalsIgnoreCase("false")) return false;
if (str.matches("-?\\d+")) return Integer.parseInt(str);
if (str.matches("-?\\d+\\.\\d+")) return Double.parseDouble(str);
return str;
default:
return null;
}
public void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
handler.applyNBTRecursively(item, section, player, panel, position);
}
private Map<String, Object> convertCompoundToMap(NBTCompound compound) {
Map<String, Object> compoundMap = new LinkedHashMap<>();
compound.getKeys().forEach(key -> compoundMap.put(key, extractNBTValue(compound, key, compound.getType(key))));
return compoundMap;
}
private void saveMapToNBTCompound(Map<String, Object> map, NBTCompound compound) {
map.forEach((key, value) -> {
if (value instanceof Map) {
saveMapToNBTCompound((Map<String, Object>) value, compound.addCompound(key));
} else {
setNBTValue(compound, key, value);
}
});
public void saveMapToYAML(Map<String, Object> map, ConfigurationSection section) {
handler.saveMapToYAML(map, section);
}
}

View File

@ -0,0 +1,117 @@
package me.rockyhawk.commandpanels.ioclasses.nbt;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class PersistentDataHandler implements NBTHandler {
private final Plugin plugin;
public PersistentDataHandler(Plugin plugin) {
this.plugin = plugin;
}
public boolean hasSameNBT(ItemStack one, ItemStack two) {
return getAllKeys(one).equals(getAllKeys(two)); // basic compare
}
public boolean hasNBT(ItemStack item, String key) {
if (item == null || item.getType() == Material.AIR) return false;
PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer();
return container.has(new NamespacedKey(plugin, key), PersistentDataType.STRING);
}
public ItemStack setNBT(ItemStack item, String key, Object value) {
if (item == null || item.getType() == Material.AIR) return item;
ItemMeta meta = item.getItemMeta();
meta.getPersistentDataContainer().set(
new NamespacedKey(plugin, key), PersistentDataType.STRING, value.toString()
);
item.setItemMeta(meta);
return item;
}
public Object getNBTValue(ItemStack item, String key) {
if (item == null || item.getType() == Material.AIR) return null;
PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer();
return container.get(new NamespacedKey(plugin, key), PersistentDataType.STRING);
}
private String getAllKeys(ItemStack item) {
if (item == null || item.getType() == Material.AIR) return "";
PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer();
return container.getKeys().toString(); // basic comparison
}
public void applyNBTRecursively(ItemStack item, ConfigurationSection section, Player player, Panel panel, PanelPosition position) {
ItemMeta meta = item.getItemMeta();
if (meta == null) return;
PersistentDataContainer container = meta.getPersistentDataContainer();
applySectionToContainer(container, section);
item.setItemMeta(meta);
}
private void applySectionToContainer(PersistentDataContainer container, ConfigurationSection section) {
for (String key : section.getKeys(false)) {
Object value = section.get(key);
if (section.isConfigurationSection(key)) {
NamespacedKey nsKey = new NamespacedKey("myplugin", key);
PersistentDataContainer subContainer = container.getAdapterContext().newPersistentDataContainer();
applySectionToContainer(subContainer, section.getConfigurationSection(key));
container.set(nsKey, PersistentDataType.TAG_CONTAINER, subContainer);
} else {
setValueInContainer(container, key, value);
}
}
}
private void setValueInContainer(PersistentDataContainer container, String key, Object value) {
NamespacedKey nsKey = new NamespacedKey("myplugin", key);
if (value instanceof Boolean) {
container.set(nsKey, PersistentDataType.BYTE, (byte) ((Boolean) value ? 1 : 0));
} else if (value instanceof Integer) {
container.set(nsKey, PersistentDataType.INTEGER, (Integer) value);
} else if (value instanceof Double) {
container.set(nsKey, PersistentDataType.DOUBLE, (Double) value);
} else if (value instanceof Long) {
container.set(nsKey, PersistentDataType.LONG, (Long) value);
} else if (value instanceof Float) {
container.set(nsKey, PersistentDataType.FLOAT, (Float) value);
} else if (value instanceof Byte) {
container.set(nsKey, PersistentDataType.BYTE, (Byte) value);
} else {
container.set(nsKey, PersistentDataType.STRING, value.toString());
}
}
public void saveMapToYAML(Map<String, Object> map, ConfigurationSection section) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
if (value instanceof Map) {
ConfigurationSection newSection = section.createSection(entry.getKey());
saveMapToYAML((Map<String, Object>) value, newSection);
} else if (value instanceof Byte) {
byte byteValue = (Byte) value;
if (byteValue == 1 || byteValue == 0) {
section.set(entry.getKey(), byteValue == 1);
} else {
section.set(entry.getKey(), byteValue);
}
} else {
section.set(entry.getKey(), value);
}
}
}
}