From d594054c60ef40e67b644c11b4564db915806019 Mon Sep 17 00:00:00 2001 From: rockyhawk64 Date: Sat, 26 Dec 2020 22:31:54 +1100 Subject: [PATCH] v3.14.4.2 --- .idea/libraries/spigot_1_13_2.xml | 4 +- .../{spigot_1_16_1.xml => spigot_1_16_4.xml} | 6 +- Command Panels.iml | 4 +- resource/plugin.yml | 2 +- resource/template.yml | 19 + .../commandpanels/CommandPanels.java | 14 +- .../classresources/CommandTags.java | 17 +- .../classresources/ExecuteOpenVoids.java | 2 +- .../classresources/ItemCreation.java | 18 +- .../classresources/OpenEditorGuis.java | 8 +- .../classresources/Placeholders.java | 15 +- .../commandpanels/commands/Commandpanel.java | 15 +- .../datamanager/PanelDataLoader.java | 40 + .../interactives/Commandpanelrefresher.java | 36 +- .../commandpanels/ioclasses/NBTEditor.java | 1427 +++++++++++++++++ .../openpanelsmanager/OpenGUI.java | 2 + .../openwithitem/HotbarItemLoader.java | 2 +- .../openwithitem/UtilsOpenWithItem.java | 8 +- 18 files changed, 1577 insertions(+), 62 deletions(-) rename .idea/libraries/{spigot_1_16_1.xml => spigot_1_16_4.xml} (65%) create mode 100644 resource/template.yml create mode 100644 src/me/rockyhawk/commandpanels/datamanager/PanelDataLoader.java create mode 100644 src/me/rockyhawk/commandpanels/ioclasses/NBTEditor.java diff --git a/.idea/libraries/spigot_1_13_2.xml b/.idea/libraries/spigot_1_13_2.xml index 52b58e2..4b53a18 100644 --- a/.idea/libraries/spigot_1_13_2.xml +++ b/.idea/libraries/spigot_1_13_2.xml @@ -1,11 +1,11 @@ - + - + \ No newline at end of file diff --git a/.idea/libraries/spigot_1_16_1.xml b/.idea/libraries/spigot_1_16_4.xml similarity index 65% rename from .idea/libraries/spigot_1_16_1.xml rename to .idea/libraries/spigot_1_16_4.xml index 1394be5..c80a683 100644 --- a/.idea/libraries/spigot_1_16_1.xml +++ b/.idea/libraries/spigot_1_16_4.xml @@ -1,11 +1,11 @@ - + - + - + \ No newline at end of file diff --git a/Command Panels.iml b/Command Panels.iml index 5b080d5..c1aaf65 100644 --- a/Command Panels.iml +++ b/Command Panels.iml @@ -11,11 +11,11 @@ - - + + \ No newline at end of file diff --git a/resource/plugin.yml b/resource/plugin.yml index e974015..0374b3d 100644 --- a/resource/plugin.yml +++ b/resource/plugin.yml @@ -1,4 +1,4 @@ -version: 3.14.4.1 +version: 3.14.4.2 main: me.rockyhawk.commandpanels.CommandPanels name: CommandPanels author: RockyHawk diff --git a/resource/template.yml b/resource/template.yml new file mode 100644 index 0000000..bd3b97f --- /dev/null +++ b/resource/template.yml @@ -0,0 +1,19 @@ +# |------------------------------------------------------------------------ +# | CommandPanels Template File +# | By RockyHawk v1.0 +# | https://www.spigotmc.org/resources/command-panels-custom-guis.67788/ +# |------------------------------------------------------------------------ +panels: + template: + perm: default + rows: 1 + title: '&8Template Panel' + empty: GLASS_PANE + item: + '4': + material: COBBLESTONE + stack: 1 + name: '&fClick Me!' + commands: + - msg= You clicked the item! + - cpc \ No newline at end of file diff --git a/src/me/rockyhawk/commandpanels/CommandPanels.java b/src/me/rockyhawk/commandpanels/CommandPanels.java index 69d618d..f415173 100644 --- a/src/me/rockyhawk/commandpanels/CommandPanels.java +++ b/src/me/rockyhawk/commandpanels/CommandPanels.java @@ -1,21 +1,18 @@ package me.rockyhawk.commandpanels; import java.io.*; -import java.net.InetSocketAddress; -import java.net.Socket; import java.util.*; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.bencodez.votingplugin.user.UserManager; import me.clip.placeholderapi.PlaceholderAPI; -import me.realized.tokenmanager.api.TokenManager; import me.rockyhawk.commandpanels.classresources.*; import me.rockyhawk.commandpanels.commands.*; import me.rockyhawk.commandpanels.completetabs.CpTabComplete; import me.rockyhawk.commandpanels.customcommands.CommandPlaceholderLoader; import me.rockyhawk.commandpanels.customcommands.Commandpanelcustom; +import me.rockyhawk.commandpanels.datamanager.PanelDataLoader; import me.rockyhawk.commandpanels.generatepanels.Commandpanelsgenerate; import me.rockyhawk.commandpanels.generatepanels.GenUtils; import me.rockyhawk.commandpanels.generatepanels.TabCompleteGenerate; @@ -71,6 +68,7 @@ public class CommandPanels extends JavaPlugin { //get alternate classes public CommandTags commandTags = new CommandTags(this); + public PanelDataLoader panelData = new PanelDataLoader(this); public Placeholders placeholders = new Placeholders(this); public OpenEditorGuis editorGuis = new OpenEditorGuis(this); public ExecuteOpenVoids openVoids = new ExecuteOpenVoids(this); @@ -86,12 +84,15 @@ public class CommandPanels extends JavaPlugin { public File panelsf; public YamlConfiguration blockConfig; //where panel block locations are stored + public YamlConfiguration dataConfig; //where arbitrary data is stored for players public void onEnable() { Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loading..."); + //register config files this.panelsf = new File(this.getDataFolder() + File.separator + "panels"); this.blockConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "blocks.yml")); + panelData.dataConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "data.yml")); this.config = YamlConfiguration.loadConfiguration(new File(this.getDataFolder() + File.separator + "config.yml")); //save the config.yml file @@ -170,16 +171,18 @@ public class CommandPanels extends JavaPlugin { this.getServer().getPluginManager().registerEvents(new SwapItemEvent(this), this); } - //save the example.yml file + //save the example.yml file and the template.yml file if (!this.panelsf.exists() || Objects.requireNonNull(this.panelsf.list()).length == 0) { try { FileConfiguration exampleFileConfiguration; + FileConfiguration templateFileConfiguration = YamlConfiguration.loadConfiguration(getReaderFromStream(this.getResource("template.yml"))); if(legacy.isLegacy()){ exampleFileConfiguration = YamlConfiguration.loadConfiguration(getReaderFromStream(this.getResource("exampleLegacy.yml"))); }else { exampleFileConfiguration = YamlConfiguration.loadConfiguration(getReaderFromStream(this.getResource("example.yml"))); } exampleFileConfiguration.save(new File(this.panelsf + File.separator + "example.yml")); + templateFileConfiguration.save(new File(this.panelsf + File.separator + "template.yml")); } catch (IOException var11) { Bukkit.getConsoleSender().sendMessage("[CommandPanels]" + ChatColor.RED + " WARNING: Could not save the example file!"); } @@ -212,6 +215,7 @@ public class CommandPanels extends JavaPlugin { } public void onDisable() { + panelData.saveDataFile(); if (Objects.requireNonNull(this.config.getString("config.updater.auto-update")).equalsIgnoreCase("true")) { updater.autoUpdatePlugin(this.getFile().getName()); } diff --git a/src/me/rockyhawk/commandpanels/classresources/CommandTags.java b/src/me/rockyhawk/commandpanels/classresources/CommandTags.java index 04f4724..521b53e 100644 --- a/src/me/rockyhawk/commandpanels/classresources/CommandTags.java +++ b/src/me/rockyhawk/commandpanels/classresources/CommandTags.java @@ -41,6 +41,21 @@ public class CommandTags { p.closeInventory(); break; } + case "set-data=":{ + //this will overwrite data. set-data= [data point] [data value] + plugin.panelData.setUserData(p.getUniqueId(),command.split("\\s")[1],command.split("\\s")[2],true); + break; + } + case "add-data=":{ + //this will not overwrite existing data. add-data= [data point] [data value] + plugin.panelData.setUserData(p.getUniqueId(),command.split("\\s")[1],command.split("\\s")[2],false); + break; + } + case "del-data=":{ + //this will remove data. del-data= [data point] + plugin.panelData.delUserData(p.getUniqueId(),command.split("\\s")[1]); + break; + } case "open=":{ //if player uses open= it will open the panel, with the option to add custom placeholders String panelName = commandRAW.split("\\s")[1]; @@ -560,7 +575,7 @@ public class CommandTags { //create the item to be removed ItemStack sellItem; if(command.split("\\s").length == 2) { - sellItem = plugin.itemCreate.makeItemFromConfig(plugin.openPanels.getOpenPanel(p.getName()).getConfigurationSection("custom-item." + command.split("\\s")[1]), p, true, true, true); + sellItem = plugin.itemCreate.makeItemFromConfig(plugin.openPanels.getOpenPanel(p.getName()).getConfigurationSection("custom-item." + command.split("\\s")[1]), p, true, true, false); }else{ sellItem = new ItemStack(Objects.requireNonNull(Material.matchMaterial(command.split("\\s")[1])), Integer.parseInt(command.split("\\s")[2]), id); } diff --git a/src/me/rockyhawk/commandpanels/classresources/ExecuteOpenVoids.java b/src/me/rockyhawk/commandpanels/classresources/ExecuteOpenVoids.java index 4b1f2ff..149b0ce 100644 --- a/src/me/rockyhawk/commandpanels/classresources/ExecuteOpenVoids.java +++ b/src/me/rockyhawk/commandpanels/classresources/ExecuteOpenVoids.java @@ -115,7 +115,7 @@ public class ExecuteOpenVoids { } ItemStack s; try { - s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(cf.getConfigurationSection("open-with-item")), p, false, true, true); + s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(cf.getConfigurationSection("open-with-item")), p, false, true, false); }catch(Exception n){ sender.sendMessage(plugin.papi(plugin.tag + plugin.config.getString("config.format.error") + " open-with-item: material")); return; diff --git a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java index 338519d..a78362f 100644 --- a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java +++ b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java @@ -2,6 +2,7 @@ package me.rockyhawk.commandpanels.classresources; import me.arcaniax.hdb.api.HeadDatabaseAPI; import me.rockyhawk.commandpanels.CommandPanels; +import me.rockyhawk.commandpanels.ioclasses.NBTEditor; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; import net.Indyuce.mmoitems.manager.ItemManager; @@ -36,7 +37,7 @@ public class ItemCreation { } @SuppressWarnings("deprecation") - public ItemStack makeItemFromConfig(ConfigurationSection itemSection, Player p, boolean placeholders, boolean colours, boolean hideAttributes){ + public ItemStack makeItemFromConfig(ConfigurationSection itemSection, Player p, boolean placeholders, boolean colours, boolean addNBT){ String material = plugin.papiNoColour(p,itemSection.getString("material")); try { if (Objects.requireNonNull(material).equalsIgnoreCase("AIR")) { @@ -48,6 +49,7 @@ public class ItemCreation { return null; } ItemStack s = null; + boolean hideAttributes = true; String mat; String matraw; String skullname; @@ -162,9 +164,19 @@ public class ItemCreation { //itemType values if(itemSection.contains("itemType")){ //if hidden, reverse - if(itemSection.getStringList("itemType").contains("attributes")){ - hideAttributes = !hideAttributes; + if(itemSection.getStringList("itemType").contains("noAttributes")){ + hideAttributes = false; } + if(itemSection.getStringList("itemType").contains("noNBT")){ + addNBT = false; + } + if(itemSection.getStringList("itemType").contains("placeable")){ + addNBT = false; + } + } + + if(addNBT){ + s = NBTEditor.set(s,"CommandPanels","plugin"); } if (itemSection.contains("map")) { diff --git a/src/me/rockyhawk/commandpanels/classresources/OpenEditorGuis.java b/src/me/rockyhawk/commandpanels/classresources/OpenEditorGuis.java index b7fac4d..6c1640f 100644 --- a/src/me/rockyhawk/commandpanels/classresources/OpenEditorGuis.java +++ b/src/me/rockyhawk/commandpanels/classresources/OpenEditorGuis.java @@ -38,7 +38,7 @@ public class OpenEditorGuis { panelNames.add(plugin.papi( key)); panelTitles.add(plugin.papi( Objects.requireNonNull(temp.getString("panels." + key + ".title")))); if (temp.contains("panels." + key + ".open-with-item.material")) { - panelItems.add(plugin.itemCreate.makeItemFromConfig(temp.getConfigurationSection("panels." + key + ".open-with-item"), p, false, true, true)); + panelItems.add(plugin.itemCreate.makeItemFromConfig(temp.getConfigurationSection("panels." + key + ".open-with-item"), p, false, true, false)); } else { panelItems.add(new ItemStack(Material.PAPER)); } @@ -268,7 +268,7 @@ public class OpenEditorGuis { if(cf.contains("open-with-item.material")){ hotbarItems = true; - temp = plugin.itemCreate.makeItemFromConfig(cf.getConfigurationSection("open-with-item"), p, false, true, true); + temp = plugin.itemCreate.makeItemFromConfig(cf.getConfigurationSection("open-with-item"), p, false, true, false); }else{ temp = new ItemStack(Material.REDSTONE_BLOCK, 1); } @@ -505,7 +505,7 @@ public class OpenEditorGuis { plugin.setName(temp, ChatColor.RED + "Back", null, p, true, true, true); i.setItem(27, temp); - temp = plugin.itemCreate.makeItemFromConfig(cf,p,false,false, true); + temp = plugin.itemCreate.makeItemFromConfig(cf,p,false,false, false); lore.clear(); lore.add(ChatColor.GRAY + "Click to set custom material"); lore.add(ChatColor.GRAY + "typically for custom heads"); @@ -544,7 +544,7 @@ public class OpenEditorGuis { lore.add(ChatColor.WHITE + "Compare: " + ChatColor.GRAY + cf.getString(section + ".compare")); } - temp = plugin.itemCreate.makeItemFromConfig(cf.getConfigurationSection(section),p,false,false, true); + temp = plugin.itemCreate.makeItemFromConfig(cf.getConfigurationSection(section),p,false,false, false); plugin.setName(temp, ChatColor.AQUA + section, lore, p,false, true, true); i.setItem(slot, temp); slot++; diff --git a/src/me/rockyhawk/commandpanels/classresources/Placeholders.java b/src/me/rockyhawk/commandpanels/classresources/Placeholders.java index b99dd8f..b98abff 100644 --- a/src/me/rockyhawk/commandpanels/classresources/Placeholders.java +++ b/src/me/rockyhawk/commandpanels/classresources/Placeholders.java @@ -136,7 +136,7 @@ public class Placeholders { try { //if it is a regular custom item - ItemStack confItm = plugin.itemCreate.makeItemFromConfig(plugin.openPanels.getOpenPanel(p.getName()).getConfigurationSection("custom-item." + matLoc),p,true,true, true); + ItemStack confItm = plugin.itemCreate.makeItemFromConfig(plugin.openPanels.getOpenPanel(p.getName()).getConfigurationSection("custom-item." + matLoc),p,true,true, false); if(plugin.itemCreate.isIdentical(confItm,itm)){ isIdentical = true; } @@ -176,6 +176,19 @@ public class Placeholders { break; } } + //returns value of stored data + while (str.contains("%cp-data-")) { + try { + int start = str.indexOf("%cp-data-"); + int end = str.indexOf("%", str.indexOf("%cp-data-") + 1); + String dataPoint = str.substring(start, end).replace("%cp-data-", "").replace("%", ""); + str = str.replace(str.substring(start, end) + "%", plugin.panelData.getUserData(p.getUniqueId(),dataPoint)); + }catch (Exception ex){ + plugin.debug(ex); + break; + } + } + //checks for players online while (str.contains("%cp-player-online-")) { try { int start = str.indexOf("%cp-player-online-"); diff --git a/src/me/rockyhawk/commandpanels/commands/Commandpanel.java b/src/me/rockyhawk/commandpanels/commands/Commandpanel.java index 2025b13..75dfd58 100644 --- a/src/me/rockyhawk/commandpanels/commands/Commandpanel.java +++ b/src/me/rockyhawk/commandpanels/commands/Commandpanel.java @@ -40,10 +40,11 @@ public class Commandpanel implements CommandExecutor { sender.sendMessage(plugin.papi(plugin.tag + plugin.config.getString("config.format.nopanel"))); return true; } + boolean disableCommand = false; if(cf.contains("panelType")) { if (cf.getStringList("panelType").contains("nocommand")) { //do not allow command with noCommand - return true; + disableCommand = true; } } //below will start the command, once it got the right file and panel @@ -55,7 +56,9 @@ public class Commandpanel implements CommandExecutor { if(plugin.openPanels.hasPanelOpen(plugin.getServer().getPlayer(args[1]).getName())) { plugin.openPanels.skipPanels.add(plugin.getServer().getPlayer(args[1]).getName()); } - plugin.openVoids.openCommandPanel(sender,plugin.getServer().getPlayer(args[1]),panelName,cf,true); + if(!disableCommand) { + plugin.openVoids.openCommandPanel(sender, plugin.getServer().getPlayer(args[1]), panelName, cf, true); + } }else{ sender.sendMessage(plugin.papi(plugin.tag + ChatColor.RED + "Usage: /cp [item] [player]")); } @@ -79,7 +82,9 @@ public class Commandpanel implements CommandExecutor { if(plugin.openPanels.hasPanelOpen(p.getName())) { plugin.openPanels.skipPanels.add(p.getName()); } - plugin.openVoids.openCommandPanel(sender, p, panelName, cf,false); + if(!disableCommand) { + plugin.openVoids.openCommandPanel(sender, p, panelName, cf, false); + } return true; }else if(args.length == 2){ if (args[1].equals("item")) { @@ -88,7 +93,9 @@ public class Commandpanel implements CommandExecutor { if(plugin.openPanels.hasPanelOpen(plugin.getServer().getPlayer(args[1]).getName())) { plugin.openPanels.skipPanels.add(plugin.getServer().getPlayer(args[1]).getName()); } - plugin.openVoids.openCommandPanel(sender, plugin.getServer().getPlayer(args[1]), panelName, cf,true); + if(!disableCommand) { + plugin.openVoids.openCommandPanel(sender, plugin.getServer().getPlayer(args[1]), panelName, cf, true); + } } return true; }else if(args.length == 3){ diff --git a/src/me/rockyhawk/commandpanels/datamanager/PanelDataLoader.java b/src/me/rockyhawk/commandpanels/datamanager/PanelDataLoader.java new file mode 100644 index 0000000..9175c08 --- /dev/null +++ b/src/me/rockyhawk/commandpanels/datamanager/PanelDataLoader.java @@ -0,0 +1,40 @@ +package me.rockyhawk.commandpanels.datamanager; + +import me.rockyhawk.commandpanels.CommandPanels; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class PanelDataLoader { + CommandPanels plugin; + public PanelDataLoader(CommandPanels pl) { + this.plugin = pl; + } + public YamlConfiguration dataConfig; + + public String getUserData(UUID playerUUID, String dataPoint){ + return dataConfig.getString("playerData." + playerUUID + "." + dataPoint); + } + + public void setUserData(UUID playerUUID, String dataPoint, String dataValue, boolean overwrite){ + if(!overwrite && dataConfig.isSet("playerData." + playerUUID + "." + dataPoint)){ + return; + } + dataConfig.set("playerData." + playerUUID + "." + dataPoint, dataValue); + } + + public void delUserData(UUID playerUUID, String dataPoint){ + dataConfig.set("playerData." + playerUUID + "." + dataPoint, null); + } + + public void saveDataFile(){ + try { + dataConfig.save(plugin.getDataFolder() + File.separator + "data.yml"); + } catch (IOException s) { + s.printStackTrace(); + plugin.debug(s); + } + } +} diff --git a/src/me/rockyhawk/commandpanels/interactives/Commandpanelrefresher.java b/src/me/rockyhawk/commandpanels/interactives/Commandpanelrefresher.java index 4cd4697..cf49233 100644 --- a/src/me/rockyhawk/commandpanels/interactives/Commandpanelrefresher.java +++ b/src/me/rockyhawk/commandpanels/interactives/Commandpanelrefresher.java @@ -1,6 +1,7 @@ package me.rockyhawk.commandpanels.interactives; import me.rockyhawk.commandpanels.CommandPanels; +import me.rockyhawk.commandpanels.ioclasses.NBTEditor; import org.bukkit.*; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.HumanEntity; @@ -26,13 +27,7 @@ public class Commandpanelrefresher implements Listener { return; } } - HumanEntity h = e.getPlayer(); - Player p; - if (h instanceof Player) { - p = Bukkit.getPlayer(h.getName()); - }else{ - return; - } + Player p = (Player) e.getPlayer(); if(!plugin.openPanels.hasPanelOpen(p.getName())){ return; @@ -106,30 +101,11 @@ public class Commandpanelrefresher implements Listener { p.updateInventory(); for (ItemStack playerContent : plugin.legacy.getStorageContents(p.getInventory())) { //ensure the panel item is not a placeable item - int itemSlot = 0; - for (ItemStack panelContent : panelItemList) { - if(cf.getStringList("item." + itemSlot + ".itemType").contains("placeable")){ - continue; + try { + if (NBTEditor.getString(playerContent, "plugin").equalsIgnoreCase("CommandPanels")) { + p.getInventory().removeItem(playerContent); } - if (playerContent != null && panelContent != null) { - if (!playerContent.getType().equals(Material.matchMaterial("AIR")) && !panelContent.getType().equals(Material.matchMaterial("AIR"))) { - if (playerContent.equals(panelContent)) { - boolean isOriginal = false; - for (ItemStack playerOriginalContent : playerItemList) { - if (playerOriginalContent != null && !playerOriginalContent.getType().equals(Material.matchMaterial("AIR"))) { - if (playerContent.equals(playerOriginalContent)) { - isOriginal = true; - } - } - } - if(!isOriginal) { - p.getInventory().removeItem(playerContent); - } - } - } - } - itemSlot++; - } + }catch(Exception ignore){} } }catch(Exception e){ //oof diff --git a/src/me/rockyhawk/commandpanels/ioclasses/NBTEditor.java b/src/me/rockyhawk/commandpanels/ioclasses/NBTEditor.java new file mode 100644 index 0000000..75ea7c7 --- /dev/null +++ b/src/me/rockyhawk/commandpanels/ioclasses/NBTEditor.java @@ -0,0 +1,1427 @@ +package me.rockyhawk.commandpanels.ioclasses; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +/** + * Sets/Gets NBT tags from ItemStacks + * Spigot: https://www.spigotmc.org/threads/269621/ + * + * @version 7.16.1 + * @author BananaPuncher714 + */ +@SuppressWarnings("deprecation") +public final class NBTEditor { + private static final Map< String, Class > classCache; + private static final Map< String, Method > methodCache; + private static final Map< Class< ? >, Constructor< ? >> constructorCache; + private static final Map< Class< ? >, Class< ? > > NBTClasses; + private static final Map< Class< ? >, Field > NBTTagFieldCache; + private static Field NBTListData; + private static Field NBTCompoundMap; + private static final String VERSION; + private static final MinecraftVersion LOCAL_VERSION; + + static { + VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + LOCAL_VERSION = MinecraftVersion.get( VERSION ); + + classCache = new HashMap< String, Class >(); + try { + classCache.put( "NBTBase", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTBase" ) ); + classCache.put( "NBTTagCompound", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagCompound" ) ); + classCache.put( "NBTTagList", Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagList" ) ); + classCache.put( "MojangsonParser", Class.forName( "net.minecraft.server." + VERSION + "." + "MojangsonParser" ) ); + + classCache.put( "ItemStack", Class.forName( "net.minecraft.server." + VERSION + "." + "ItemStack" ) ); + classCache.put( "CraftItemStack", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".inventory." + "CraftItemStack" ) ); + classCache.put( "CraftMetaSkull", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".inventory." + "CraftMetaSkull" ) ); + + classCache.put( "Entity", Class.forName( "net.minecraft.server." + VERSION + "." + "Entity" ) ); + classCache.put( "CraftEntity", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".entity." + "CraftEntity" ) ); + classCache.put( "EntityLiving", Class.forName( "net.minecraft.server." + VERSION + "." + "EntityLiving" ) ); + + classCache.put( "CraftWorld", Class.forName( "org.bukkit.craftbukkit." + VERSION + "." + "CraftWorld" ) ); + classCache.put( "CraftBlockState", Class.forName( "org.bukkit.craftbukkit." + VERSION + ".block." + "CraftBlockState" ) ); + classCache.put( "BlockPosition", Class.forName( "net.minecraft.server." + VERSION + "." + "BlockPosition" ) ); + classCache.put( "TileEntity", Class.forName( "net.minecraft.server." + VERSION + "." + "TileEntity" ) ); + classCache.put( "World", Class.forName( "net.minecraft.server." + VERSION + "." + "World" ) ); + classCache.put( "IBlockData", Class.forName( "net.minecraft.server." + VERSION + "." + "IBlockData" ) ); + + classCache.put( "TileEntitySkull", Class.forName( "net.minecraft.server." + VERSION + "." + "TileEntitySkull" ) ); + + classCache.put( "GameProfile", Class.forName( "com.mojang.authlib.GameProfile" ) ); + classCache.put( "Property", Class.forName( "com.mojang.authlib.properties.Property" ) ); + classCache.put( "PropertyMap", Class.forName( "com.mojang.authlib.properties.PropertyMap" ) ); + } catch ( ClassNotFoundException e ) { + e.printStackTrace(); + } + + NBTClasses = new HashMap< Class< ? >, Class< ? > >(); + try { + NBTClasses.put( Byte.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByte" ) ); + NBTClasses.put( Boolean.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByte" ) ); + NBTClasses.put( String.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagString" ) ); + NBTClasses.put( Double.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagDouble" ) ); + NBTClasses.put( Integer.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagInt" ) ); + NBTClasses.put( Long.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagLong" ) ); + NBTClasses.put( Short.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagShort" ) ); + NBTClasses.put( Float.class, Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagFloat" ) ); + NBTClasses.put( Class.forName( "[B" ), Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagByteArray" ) ); + NBTClasses.put( Class.forName( "[I" ), Class.forName( "net.minecraft.server." + VERSION + "." + "NBTTagIntArray" ) ); + } catch ( ClassNotFoundException e ) { + e.printStackTrace(); + } + + methodCache = new HashMap< String, Method >(); + try { + methodCache.put( "get", getNMSClass( "NBTTagCompound" ).getMethod( "get", String.class ) ); + methodCache.put( "set", getNMSClass( "NBTTagCompound" ).getMethod( "set", String.class, getNMSClass( "NBTBase" ) ) ); + methodCache.put( "hasKey", getNMSClass( "NBTTagCompound" ).getMethod( "hasKey", String.class ) ); + methodCache.put( "setIndex", getNMSClass( "NBTTagList" ).getMethod( "a", int.class, getNMSClass( "NBTBase" ) ) ); + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + methodCache.put( "getTypeId", getNMSClass( "NBTBase" ).getMethod( "getTypeId" ) ); + methodCache.put( "add", getNMSClass( "NBTTagList" ).getMethod( "add", int.class, getNMSClass( "NBTBase" ) ) ); + } else { + methodCache.put( "add", getNMSClass( "NBTTagList" ).getMethod( "add", getNMSClass( "NBTBase" ) ) ); + } + methodCache.put( "size", getNMSClass( "NBTTagList" ).getMethod( "size" ) ); + + if ( LOCAL_VERSION == MinecraftVersion.v1_8 ) { + methodCache.put( "listRemove", getNMSClass( "NBTTagList" ).getMethod( "a", int.class ) ); + } else { + methodCache.put( "listRemove", getNMSClass( "NBTTagList" ).getMethod( "remove", int.class ) ); + } + methodCache.put( "remove", getNMSClass( "NBTTagCompound" ).getMethod( "remove", String.class ) ); + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_13 ) ) { + methodCache.put( "getKeys", getNMSClass( "NBTTagCompound" ).getMethod( "getKeys" ) ); + } else { + methodCache.put( "getKeys", getNMSClass( "NBTTagCompound" ).getMethod( "c" ) ); + } + + methodCache.put( "hasTag", getNMSClass( "ItemStack" ).getMethod( "hasTag" ) ); + methodCache.put( "getTag", getNMSClass( "ItemStack" ).getMethod( "getTag" ) ); + methodCache.put( "setTag", getNMSClass( "ItemStack" ).getMethod( "setTag", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "asNMSCopy", getNMSClass( "CraftItemStack" ).getMethod( "asNMSCopy", ItemStack.class ) ); + methodCache.put( "asBukkitCopy", getNMSClass( "CraftItemStack" ).getMethod( "asBukkitCopy", getNMSClass( "ItemStack" ) ) ); + + methodCache.put( "getEntityHandle", getNMSClass( "CraftEntity" ).getMethod( "getHandle" ) ); + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + methodCache.put( "getEntityTag", getNMSClass( "Entity" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "setEntityTag", getNMSClass( "Entity" ).getMethod( "load", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "getEntityTag", getNMSClass( "Entity" ).getMethod( "c", getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "setEntityTag", getNMSClass( "Entity" ).getMethod( "f", getNMSClass( "NBTTagCompound" ) ) ); + } + + methodCache.put( "save", getNMSClass( "ItemStack" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + + if ( LOCAL_VERSION.lessThanOrEqualTo( MinecraftVersion.v1_10 ) ) { + methodCache.put( "createStack", getNMSClass( "ItemStack" ).getMethod( "createStack", getNMSClass( "NBTTagCompound" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_13 ) ) { + methodCache.put( "createStack", getNMSClass( "ItemStack" ).getMethod( "a", getNMSClass( "NBTTagCompound" ) ) ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "load", getNMSClass( "IBlockData" ), getNMSClass( "NBTTagCompound" ) ) ); + methodCache.put( "getType", getNMSClass( "World" ).getMethod( "getType", getNMSClass( "BlockPosition" ) ) ); + } else if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_12 ) ) { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "load", getNMSClass( "NBTTagCompound" ) ) ); + } else { + methodCache.put( "setTileTag", getNMSClass( "TileEntity" ).getMethod( "a", getNMSClass( "NBTTagCompound" ) ) ); + } + methodCache.put( "getTileEntity", getNMSClass( "World" ).getMethod( "getTileEntity", getNMSClass( "BlockPosition" ) ) ); + methodCache.put( "getWorldHandle", getNMSClass( "CraftWorld" ).getMethod( "getHandle" ) ); + + methodCache.put( "setGameProfile", getNMSClass( "TileEntitySkull" ).getMethod( "setGameProfile", getNMSClass( "GameProfile" ) ) ); + methodCache.put( "getProperties", getNMSClass( "GameProfile" ).getMethod( "getProperties" ) ); + methodCache.put( "getName", getNMSClass( "Property" ).getMethod( "getName" ) ); + methodCache.put( "getValue", getNMSClass( "Property" ).getMethod( "getValue" ) ); + methodCache.put( "values", getNMSClass( "PropertyMap" ).getMethod( "values" ) ); + methodCache.put( "put", getNMSClass( "PropertyMap" ).getMethod( "put", Object.class, Object.class ) ); + + methodCache.put( "loadNBTTagCompound", getNMSClass( "MojangsonParser" ).getMethod( "parse", String.class ) ); + } catch( Exception e ) { + e.printStackTrace(); + } + + try { + methodCache.put( "getTileTag", getNMSClass( "TileEntity" ).getMethod( "save", getNMSClass( "NBTTagCompound" ) ) ); + } catch( NoSuchMethodException exception ) { + try { + methodCache.put( "getTileTag", getNMSClass( "TileEntity" ).getMethod( "b", getNMSClass( "NBTTagCompound" ) ) ); + } catch ( Exception exception2 ) { + exception2.printStackTrace(); + } + } catch( Exception exception ) { + exception.printStackTrace(); + } + + try { + methodCache.put( "setProfile", getNMSClass( "CraftMetaSkull" ).getDeclaredMethod( "setProfile", getNMSClass( "GameProfile" ) ) ); + methodCache.get( "setProfile" ).setAccessible( true ); + } catch( NoSuchMethodException exception ) { + // The method doesn't exist, so it's before 1.15.2 + } + + constructorCache = new HashMap< Class< ? >, Constructor< ? > >(); + try { + constructorCache.put( getNBTTag( Byte.class ), getNBTTag( Byte.class ).getDeclaredConstructor( byte.class ) ); + constructorCache.put( getNBTTag( Boolean.class ), getNBTTag( Boolean.class ).getDeclaredConstructor( byte.class ) ); + constructorCache.put( getNBTTag( String.class ), getNBTTag( String.class ).getDeclaredConstructor( String.class ) ); + constructorCache.put( getNBTTag( Double.class ), getNBTTag( Double.class ).getDeclaredConstructor( double.class ) ); + constructorCache.put( getNBTTag( Integer.class ), getNBTTag( Integer.class ).getDeclaredConstructor( int.class ) ); + constructorCache.put( getNBTTag( Long.class ), getNBTTag( Long.class ).getDeclaredConstructor( long.class ) ); + constructorCache.put( getNBTTag( Float.class ), getNBTTag( Float.class ).getDeclaredConstructor( float.class ) ); + constructorCache.put( getNBTTag( Short.class ), getNBTTag( Short.class ).getDeclaredConstructor( short.class ) ); + constructorCache.put( getNBTTag( Class.forName( "[B" ) ), getNBTTag( Class.forName( "[B" ) ).getDeclaredConstructor( Class.forName( "[B" ) ) ); + constructorCache.put( getNBTTag( Class.forName( "[I" ) ), getNBTTag( Class.forName( "[I" ) ).getDeclaredConstructor( Class.forName( "[I" ) ) ); + + // This is for 1.15 since Mojang decided to make the constructors private + for ( Constructor< ? > cons : constructorCache.values() ) { + cons.setAccessible( true ); + } + + constructorCache.put( getNMSClass( "BlockPosition" ), getNMSClass( "BlockPosition" ).getConstructor( int.class, int.class, int.class ) ); + + constructorCache.put( getNMSClass( "GameProfile" ), getNMSClass( "GameProfile" ).getConstructor( UUID.class, String.class ) ); + constructorCache.put( getNMSClass( "Property" ), getNMSClass( "Property" ).getConstructor( String.class, String.class ) ); + + if ( LOCAL_VERSION == MinecraftVersion.v1_11 || LOCAL_VERSION == MinecraftVersion.v1_12 ) { + constructorCache.put( getNMSClass( "ItemStack" ), getNMSClass( "ItemStack" ).getConstructor( getNMSClass( "NBTTagCompound" ) ) ); + } + } catch( Exception e ) { + e.printStackTrace(); + } + + NBTTagFieldCache = new HashMap< Class< ? >, Field >(); + try { + for ( Class< ? > clazz : NBTClasses.values() ) { + Field data = clazz.getDeclaredField( "data" ); + data.setAccessible( true ); + NBTTagFieldCache.put( clazz, data ); + } + } catch( Exception e ) { + e.printStackTrace(); + } + + try { + NBTListData = getNMSClass( "NBTTagList" ).getDeclaredField( "list" ); + NBTListData.setAccessible( true ); + NBTCompoundMap = getNMSClass( "NBTTagCompound" ).getDeclaredField( "map" ); + NBTCompoundMap.setAccessible( true ); + } catch( Exception e ) { + e.printStackTrace(); + } + } + + private static Class< ? > getNBTTag( Class< ? > primitiveType ) { + if ( NBTClasses.containsKey( primitiveType ) ) + return NBTClasses.get( primitiveType ); + return primitiveType; + } + + private static Object getNBTVar( Object object ) { + if ( object == null ) { + return null; + } + Class< ? > clazz = object.getClass(); + try { + if ( NBTTagFieldCache.containsKey( clazz ) ) { + return NBTTagFieldCache.get( clazz ).get( object ); + } + } catch ( Exception exception ) { + exception.printStackTrace(); + } + return null; + } + + private static Method getMethod( String name ) { + return methodCache.containsKey( name ) ? methodCache.get( name ) : null; + } + + private static Constructor< ? > getConstructor( Class< ? > clazz ) { + return constructorCache.containsKey( clazz ) ? constructorCache.get( clazz ) : null; + } + + private static Class getNMSClass(String name) { + if ( classCache.containsKey( name ) ) { + return classCache.get( name ); + } + + try { + return Class.forName("net.minecraft.server." + VERSION + "." + name); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + private static String getMatch( String string, String regex ) { + Pattern pattern = Pattern.compile( regex ); + Matcher matcher = pattern.matcher( string ); + if ( matcher.find() ) { + return matcher.group( 1 ); + } else { + return null; + } + } + + private static Object createItemStack( Object compound ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { + if ( LOCAL_VERSION == MinecraftVersion.v1_11 || LOCAL_VERSION == MinecraftVersion.v1_12 ) { + return getConstructor( getNMSClass( "ItemStack" ) ).newInstance( compound ); + } + return getMethod( "createStack" ).invoke( null, compound ); + } + + /** + * Gets the Bukkit version + * + * @return + * The Bukkit version in standard package format + */ + public static String getVersion() { + return VERSION; + } + + public static MinecraftVersion getMinecraftVersion() { + return LOCAL_VERSION; + } + + /** + * Creates a skull with the given url as the skin + * + * @param skinURL + * The URL of the skin, must be from mojang + * @return + * An item stack with count of 1 + */ + public static ItemStack getHead( String skinURL ) { + Material material = Material.getMaterial( "SKULL_ITEM" ); + if ( material == null ) { + // Most likely 1.13 materials + material = Material.getMaterial( "PLAYER_HEAD" ); + } + ItemStack head = new ItemStack( material, 1, ( short ) 3 ); + if ( skinURL == null || skinURL.isEmpty() ) { + return head; + } + ItemMeta headMeta = head.getItemMeta(); + Object profile = null; + try { + profile = getConstructor( getNMSClass( "GameProfile" ) ).newInstance( UUID.randomUUID(), null ); + Object propertyMap = getMethod( "getProperties" ).invoke( profile ); + Object textureProperty = getConstructor( getNMSClass( "Property" ) ).newInstance( "textures", new String( Base64.getEncoder().encode( String.format( "{textures:{SKIN:{\"url\":\"%s\"}}}", skinURL ).getBytes() ) ) ); + getMethod( "put" ).invoke( propertyMap, "textures", textureProperty ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e1 ) { + e1.printStackTrace(); + } + + if ( methodCache.containsKey( "setProfile" ) ) { + try { + getMethod( "setProfile" ).invoke( headMeta, profile ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + } else { + Field profileField = null; + try { + profileField = headMeta.getClass().getDeclaredField("profile"); + } catch ( NoSuchFieldException | SecurityException e ) { + e.printStackTrace(); + } + profileField.setAccessible(true); + try { + profileField.set(headMeta, profile); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + head.setItemMeta(headMeta); + return head; + } + + /** + * Fetches the texture of a skull + * + * @param head + * The item stack itself + * @return + * The URL of the texture + */ + public static String getTexture( ItemStack head ) { + ItemMeta meta = head.getItemMeta(); + Field profileField = null; + try { + profileField = meta.getClass().getDeclaredField("profile"); + } catch ( NoSuchFieldException | SecurityException e ) { + e.printStackTrace(); + throw new IllegalArgumentException( "Item is not a player skull!" ); + } + profileField.setAccessible(true); + try { + Object profile = profileField.get( meta ); + if ( profile == null ) { + return null; + } + + Collection< Object > properties = ( Collection< Object > ) getMethod( "values" ).invoke( getMethod( "getProperties" ).invoke( profile ) ); + for ( Object prop : properties ) { + if ( "textures".equals( getMethod( "getName" ).invoke( prop ) ) ) { + String texture = new String( Base64.getDecoder().decode( ( String ) getMethod( "getValue" ).invoke( prop ) ) ); + return getMatch( texture, "\\{\"url\":\"(.*?)\"\\}" ); + } + } + return null; + } catch ( IllegalArgumentException | IllegalAccessException | SecurityException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Gets an NBT tag in a given item with the specified keys + * + * @param item + * The itemstack to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getItemTag( ItemStack item, Object... keys ) { + try { + return getTag( getCompound( item ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( ItemStack item ) { + if ( item == null ) { + return null; + } + try { + Object stack = null; + stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = null; + + if ( getMethod( "hasTag" ).invoke( stack ).equals( true ) ) { + tag = getMethod( "getTag" ).invoke( stack ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + } + + return tag; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the item provided. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param item + * Itemstack + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getItemNBTTag( ItemStack item, Object... keys ) { + if ( item == null ) { + return null; + } + try { + Object stack = null; + stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + tag = getMethod( "save" ).invoke( stack, tag ); + + return getNBTTag( tag, keys ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an item with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param item + * The itemstack to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + * @return + * A new ItemStack with the updated NBT tags + */ + private static ItemStack setItemTag( ItemStack item, Object value, Object... keys ) { + if ( item == null ) { + return null; + } + try { + Object stack = getMethod( "asNMSCopy" ).invoke( null, item ); + + Object tag = null; + + if ( getMethod( "hasTag" ).invoke( stack ).equals( true ) ) { + tag = getMethod( "getTag" ).invoke( stack ); + } else { + tag = getNMSClass( "NBTTagCompound" ).newInstance(); + } + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + getMethod( "setTag" ).invoke( stack, tag ); + return ( ItemStack ) getMethod( "asBukkitCopy" ).invoke( null, stack ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Constructs an ItemStack from a given NBTCompound + * + * @param compound + * An NBTCompound following an ItemStack structure + * @return + * A new ItemStack + */ + public static ItemStack getItemFromTag( NBTCompound compound ) { + if ( compound == null ) { + return null; + } + try { + Object tag = compound.tag; + Object count = getTag( tag, "Count" ); + Object id = getTag( tag, "id" ); + if ( count == null || id == null ) { + return null; + } + if ( count instanceof Byte && id instanceof String ) { + return ( ItemStack ) getMethod( "asBukkitCopy" ).invoke( null, createItemStack( tag ) ); + } + return null; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBT tag in a given entity with the specified keys + * + * @param entity + * The entity to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getEntityTag( Entity entity, Object... keys ) { + try { + return getTag( getCompound( entity ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( Entity entity ) { + if ( entity == null ) { + return entity; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + return tag; + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the entity provided. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param entity + * The Bukkit entity provided + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getEntityNBTTag( Entity entity, Object...keys ) { + if ( entity == null ) { + return null; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + return getNBTTag( tag, keys ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an entity with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param entity + * The entity to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + */ + private static void setEntityTag( Entity entity, Object value, Object... keys ) { + if ( entity == null ) { + return; + } + try { + Object NMSEntity = getMethod( "getEntityHandle" ).invoke( entity ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance() ; + + getMethod( "getEntityTag" ).invoke( NMSEntity, tag ); + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + getMethod( "setEntityTag" ).invoke( NMSEntity, tag ); + } catch ( Exception exception ) { + exception.printStackTrace(); + return; + } + } + + /** + * Gets an NBT tag in a given block with the specified keys. Use {@link #getNBTCompound(Object, Object...)} instead. + * + * @param block + * The block to get the keys from + * @param keys + * The keys to fetch; an integer after a key value indicates that it should get the nth place of + * the previous compound because it is a list; + * @return + * The item represented by the keys, and an integer if it is showing how long a list is. + */ + private static Object getBlockTag( Block block, Object... keys ) { + try { + return getTag( getCompound( block ), keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + // Gets the NBTTagCompound + private static Object getCompound( Block block ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return null; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + + return tag; + } catch( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Gets an NBTCompound from the block provided + * + * @param block + * The block provided + * @param keys + * Keys in descending order + * @return + * An NBTCompound + */ + private static NBTCompound getBlockNBTTag( Block block, Object... keys ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return null; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + + return getNBTTag( tag, keys ); + } catch( Exception exception ) { + exception.printStackTrace(); + return null; + } + } + + /** + * Sets an NBT tag in an block with the provided keys and value + * Should use the {@link #set(Object, Object, Object...)} method instead + * + * @param block + * The block to set + * @param value + * The value to set + * @param keys + * The keys to set, String for NBTCompound, int or null for an NBTTagList + */ + private static void setBlockTag( Block block, Object value, Object... keys ) { + try { + if ( block == null || !getNMSClass( "CraftBlockState" ).isInstance( block.getState() ) ) { + return; + } + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + Object tag = getNMSClass( "NBTTagCompound" ).newInstance(); + + getMethod( "getTileTag" ).invoke( tileEntity, tag ); + + if ( keys.length == 0 && value instanceof NBTCompound ) { + tag = ( ( NBTCompound ) value ).tag; + } else { + setTag( tag, value, keys ); + } + + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_16 ) ) { + getMethod( "setTileTag" ).invoke( tileEntity, getMethod( "getType" ).invoke( nmsWorld, blockPosition ), tag ); + } else { + getMethod( "setTileTag" ).invoke( tileEntity, tag ); + } + } catch( Exception exception ) { + exception.printStackTrace(); + return; + } + } + + /** + * Sets the texture of a skull block + * + * @param block + * The block, must be a skull + * @param texture + * The URL of the skin + */ + public static void setSkullTexture( Block block, String texture ) { + try { + Object profile = getConstructor( getNMSClass( "GameProfile" ) ).newInstance( UUID.randomUUID(), null ); + Object propertyMap = getMethod( "getProperties" ).invoke( profile ); + Object textureProperty = getConstructor( getNMSClass( "Property" ) ).newInstance( "textures", new String( Base64.getEncoder().encode( String.format( "{textures:{SKIN:{\"url\":\"%s\"}}}", texture ).getBytes() ) ) ); + getMethod( "put" ).invoke( propertyMap, "textures", textureProperty ); + + Location location = block.getLocation(); + + Object blockPosition = getConstructor( getNMSClass( "BlockPosition" ) ).newInstance( location.getBlockX(), location.getBlockY(), location.getBlockZ() ); + + Object nmsWorld = getMethod( "getWorldHandle" ).invoke( location.getWorld() ); + + Object tileEntity = getMethod( "getTileEntity" ).invoke( nmsWorld, blockPosition ); + + getMethod( "setGameProfile" ).invoke( tileEntity, profile ); + } catch( Exception exception ) { + exception.printStackTrace(); + } + } + + private static Object getValue( Object object, Object... keys ) { + if ( object instanceof ItemStack ) { + return getItemTag( ( ItemStack ) object, keys ); + } else if ( object instanceof Entity ) { + return getEntityTag( ( Entity ) object, keys ); + } else if ( object instanceof Block ) { + return getBlockTag( ( Block ) object, keys ); + } else if ( object instanceof NBTCompound ) { + try { + return getTag( ( ( NBTCompound ) object ).tag, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + } + + /** + * Gets an NBTCompound from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An NBTCompound, or null if none is stored at the provided location + */ + public static NBTCompound getNBTCompound( Object object, Object... keys ) { + if ( object instanceof ItemStack ) { + return getItemNBTTag( ( ItemStack ) object, keys ); + } else if ( object instanceof Entity ) { + return getEntityNBTTag( ( Entity ) object, keys ); + } else if ( object instanceof Block ) { + return getBlockNBTTag( ( Block ) object, keys ); + } else if ( object instanceof NBTCompound ) { + try { + return getNBTTag( ( ( NBTCompound ) object ).tag, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( object ) ) { + try { + return getNBTTag( object, keys ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + } + + /** + * Gets a string from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A string, or null if none is stored at the provided location + */ + public static String getString( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof String ? ( String ) result : null; + } + + /** + * Gets an int from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An integer, or 0 if none is stored at the provided location + */ + public static int getInt( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Integer ? ( int ) result : 0; + } + + /** + * Gets a double from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A double, or 0 if none is stored at the provided location + */ + public static double getDouble( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Double ? ( double ) result : 0; + } + + /** + * Gets a long from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A long, or 0 if none is stored at the provided location + */ + public static long getLong( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Long ? ( long ) result : 0; + } + + /** + * Gets a float from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A float, or 0 if none is stored at the provided location + */ + public static float getFloat( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Float ? ( float ) result : 0; + } + + /** + * Gets a short from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A short, or 0 if none is stored at the provided location + */ + public static short getShort( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Short ? ( short ) result : 0; + } + + /** + * Gets a byte from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A byte, or 0 if none is stored at the provided location + */ + public static byte getByte( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof Byte ? ( byte ) result : 0; + } + + /** + * Gets a boolean from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A boolean or false if none is stored at the provided location + */ + public static boolean getBoolean( Object object, Object... keys ) { + return getByte( object, keys ) == 1; + } + + /** + * Gets a byte array from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A byte array, or null if none is stored at the provided location + */ + public static byte[] getByteArray( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof byte[] ? ( byte[] ) result : null; + } + + /** + * Gets an int array from an object + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * An int array, or null if none is stored at the provided location + */ + public static int[] getIntArray( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result instanceof int[] ? ( int[] ) result : null; + } + + /** + * Checks if the object contains the given key + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * Whether or not the particular tag exists, may not be a primitive + */ + public static boolean contains( Object object, Object... keys ) { + Object result = getValue( object, keys ); + return result != null; + } + + /** + * Get the keys at the specific location, if it is a compound. + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * A set of keys + */ + public static Collection< String > getKeys( Object object, Object... keys ) { + Object compound; + if ( object instanceof ItemStack ) { + compound = getCompound( ( ItemStack ) object ); + } else if ( object instanceof Entity ) { + compound = getCompound( ( Entity ) object ); + } else if ( object instanceof Block ) { + compound = getCompound( ( Block ) object ); + } else if ( object instanceof NBTCompound ) { + compound = ( ( NBTCompound ) object ).tag; + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + + try { + NBTCompound nbtCompound = getNBTTag( compound, keys ); + + Object tag = nbtCompound.tag; + if ( getNMSClass( "NBTTagCompound" ).isInstance( tag ) ) { + return ( Collection< String > ) getMethod( "getKeys" ).invoke( tag ); + } else { + return null; + } + + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + + return null; + } + + /** + * Gets the size of the list or NBTCompound at the given location. + * + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param keys + * Keys in descending order + * @return + * The size of the list or compound at the given location. + */ + public static int getSize( Object object, Object... keys ) { + Object compound; + if ( object instanceof ItemStack ) { + compound = getCompound( ( ItemStack ) object ); + } else if ( object instanceof Entity ) { + compound = getCompound( ( Entity ) object ); + } else if ( object instanceof Block ) { + compound = getCompound( ( Block ) object ); + } else if ( object instanceof NBTCompound ) { + compound = ( ( NBTCompound ) object ).tag; + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + + try { + NBTCompound nbtCompound = getNBTTag( compound, keys ); + if ( getNMSClass( "NBTTagCompound" ).isInstance( nbtCompound.tag ) ) { + return getKeys( nbtCompound ).size(); + } else if ( getNMSClass( "NBTTagList" ).isInstance( nbtCompound.tag ) ) { + return ( int ) getMethod( "size" ).invoke( nbtCompound.tag ); + } + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return 0; + } + + throw new IllegalArgumentException( "Value is not a compound or list!" ); + } + + /** + * Sets the value in the object with the given keys + * + * @param + * ItemStack, Entity, Block, or NBTCompound. + * @param object + * Must be an ItemStack, Entity, Block, or NBTCompound + * @param value + * The value to set, can be an NBTCompound + * @param keys + * The keys in descending order + * @return + * The new item stack if the object provided is an item, else original object + */ + public static < T > T set( T object, Object value, Object... keys ) { + if ( object instanceof ItemStack ) { + return ( T ) setItemTag( ( ItemStack ) object, value, keys ); + } else if ( object instanceof Entity ) { + setEntityTag( ( Entity ) object, value, keys ); + } else if ( object instanceof Block ) { + setBlockTag( ( Block ) object, value, keys ); + } else if ( object instanceof NBTCompound ) { + try { + setTag( ( ( NBTCompound ) object ).tag, value, keys ); + } catch ( InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + } + } else { + throw new IllegalArgumentException( "Object provided must be of type ItemStack, Entity, Block, or NBTCompound!" ); + } + return object; + } + + /** + * Load an NBTCompound from a String. + * + * @param json + * A String in json format. + * @return + * An NBTCompound from the String provided. May or may not be a valid ItemStack. + */ + public static NBTCompound getNBTCompound( String json ) { + return NBTCompound.fromJson( json ); + } + + /** + * Get an empty NBTCompound. + * + * @return + * A new NBTCompound that contains a NBTTagCompound object. + */ + public static NBTCompound getEmptyNBTCompound() { + try { + return new NBTCompound( getNMSClass( "NBTTagCompound" ).newInstance() ); + } catch ( InstantiationException | IllegalAccessException e ) { + e.printStackTrace(); + return null; + } + } + + private static void setTag( Object tag, Object value, Object... keys ) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object notCompound; + // Get the real value of what we want to set here + if ( value != null ) { + if ( value instanceof NBTCompound ) { + notCompound = ( ( NBTCompound ) value ).tag; + } else if ( getNMSClass( "NBTTagList" ).isInstance( value ) || getNMSClass( "NBTTagCompound" ).isInstance( value ) ) { + notCompound = value; + } else { + if ( value instanceof Boolean ) { + value = ( byte ) ( ( Boolean ) value == true ? 1 : 0 ); + } + notCompound = getConstructor( getNBTTag( value.getClass() ) ).newInstance( value ); + } + } else { + notCompound = null; + } + + Object compound = tag; + for ( int index = 0; index < keys.length - 1; index++ ) { + Object key = keys[ index ]; + Object oldCompound = compound; + if ( key instanceof Integer ) { + compound = ( ( List< ? > ) NBTListData.get( compound ) ).get( ( int ) key ); + } else if ( key != null ) { + compound = getMethod( "get" ).invoke( compound, ( String ) key ); + } + if ( compound == null || key == null ) { + if ( keys[ index + 1 ] == null || keys[ index + 1 ] instanceof Integer ) { + compound = getNMSClass( "NBTTagList" ).newInstance(); + } else { + compound = getNMSClass( "NBTTagCompound" ).newInstance(); + } + if ( oldCompound.getClass().getSimpleName().equals( "NBTTagList" ) ) { + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + getMethod( "add" ).invoke( oldCompound, getMethod( "size" ).invoke( oldCompound ), compound ); + } else { + getMethod( "add" ).invoke( oldCompound, compound ); + } + } else { + getMethod( "set" ).invoke( oldCompound, ( String ) key, compound ); + } + } + } + if ( keys.length > 0 ) { + Object lastKey = keys[ keys.length - 1 ]; + if ( lastKey == null ) { + if ( LOCAL_VERSION.greaterThanOrEqualTo( MinecraftVersion.v1_14 ) ) { + getMethod( "add" ).invoke( compound, getMethod( "size" ).invoke( compound ), notCompound ); + } else { + getMethod( "add" ).invoke( compound, notCompound ); + } + } else if ( lastKey instanceof Integer ) { + if ( notCompound == null ) { + getMethod( "listRemove" ).invoke( compound, ( int ) lastKey ); + } else { + getMethod( "setIndex" ).invoke( compound, ( int ) lastKey, notCompound ); + } + } else { + if ( notCompound == null ) { + getMethod( "remove" ).invoke( compound, ( String ) lastKey ); + } else { + getMethod( "set" ).invoke( compound, ( String ) lastKey, notCompound ); + } + } + } else { + // Add and replace all tags + if ( notCompound != null ) { + // Only if they're both an NBTTagCompound + // Can't do anything if its a list or something + if ( getNMSClass( "NBTTagCompound" ).isInstance( notCompound ) && getNMSClass( "NBTTagCompound" ).isInstance( compound ) ) + for ( String key : getKeys( notCompound ) ) { + getMethod( "set" ).invoke( compound, key, getMethod( "get" ).invoke( notCompound, key ) ); + } + } else { + // Did someone make an error? + // NBTEditor.set( something, null ); + // Not sure what to do here + } + } + } + + private static NBTCompound getNBTTag( Object tag, Object...keys ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object compound = tag; + + for ( Object key : keys ) { + if ( compound == null ) { + return null; + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( compound ) ) { + compound = getMethod( "get" ).invoke( compound, ( String ) key ); + } else if ( getNMSClass( "NBTTagList" ).isInstance( compound ) ) { + compound = ( ( List< ? > ) NBTListData.get( compound ) ).get( ( int ) key ); + } + } + return new NBTCompound( compound ); + } + + private static Object getTag( Object tag, Object... keys ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + if ( keys.length == 0 ) { + return getTags( tag ); + } + + Object notCompound = tag; + + for ( Object key : keys ) { + if ( notCompound == null ) { + return null; + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( notCompound ) ) { + notCompound = getMethod( "get" ).invoke( notCompound, ( String ) key ); + } else if ( getNMSClass( "NBTTagList" ).isInstance( notCompound ) ) { + notCompound = ( ( List< ? > ) NBTListData.get( notCompound ) ).get( ( int ) key ); + } else { + return getNBTVar( notCompound ); + } + } + if ( notCompound == null ) { + return null; + } else if ( getNMSClass( "NBTTagList" ).isInstance( notCompound ) ) { + return getTags( notCompound ); + } else if ( getNMSClass( "NBTTagCompound" ).isInstance( notCompound ) ) { + return getTags( notCompound ); + } else { + return getNBTVar( notCompound ); + } + } + + @SuppressWarnings( "unchecked" ) + private static Object getTags( Object tag ) { + Map< Object, Object > tags = new HashMap< Object, Object >(); + try { + if ( getNMSClass( "NBTTagCompound" ).isInstance( tag ) ) { + Map< String, Object > tagCompound = ( Map< String, Object > ) NBTCompoundMap.get( tag ); + for ( String key : tagCompound.keySet() ) { + Object value = tagCompound.get( key ); + if ( getNMSClass( "NBTTagEnd" ).isInstance( value ) ) { + continue; + } + tags.put( key, getTag( value ) ); + } + } else if ( getNMSClass( "NBTTagList" ).isInstance( tag ) ) { + List< Object > tagList = ( List< Object > ) NBTListData.get( tag ); + for ( int index = 0; index < tagList.size(); index++ ) { + Object value = tagList.get( index ); + if ( getNMSClass( "NBTTagEnd" ).isInstance( value ) ) { + continue; + } + tags.put( index, getTag( value ) ); + } + } else { + return getNBTVar( tag ); + } + return tags; + } catch ( Exception e ) { + e.printStackTrace(); + return tags; + } + } + + /** + * A class for holding NBTTagCompounds + */ + public static final class NBTCompound { + protected final Object tag; + + protected NBTCompound( Object tag ) { + this.tag = tag; + } + + public void set( Object value, Object... keys ) { + try { + setTag( tag, value, keys ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + /** + * The exact same as the toString method + * + * @return + * Convert the compound to a string. + */ + public String toJson() { + return tag.toString(); + } + + public static NBTCompound fromJson( String json ) { + try { + return new NBTCompound( getMethod( "loadNBTTagCompound" ).invoke( null, json ) ); + } catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) { + e.printStackTrace(); + return null; + } + } + + @Override + public String toString() { + return tag.toString(); + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + + @Override + public boolean equals( Object obj ) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NBTCompound other = (NBTCompound) obj; + if (tag == null) { + if (other.tag != null) + return false; + } else if (!tag.equals(other.tag)) + return false; + return true; + } + } + + /** + * Minecraft variables as enums + * + * @author BananaPuncher714 + */ + public enum MinecraftVersion { + v1_8( "1_8", 0 ), + v1_9( "1_9", 1 ), + v1_10( "1_10", 2 ), + v1_11( "1_11", 3 ), + v1_12( "1_12", 4 ), + v1_13( "1_13", 5 ), + v1_14( "1_14", 6 ), + v1_15( "1_15", 7 ), + v1_16( "1_16", 8 ), + v1_17( "1_17", 9 ), + v1_18( "1_18", 10 ), + v1_19( "1_19", 11 ); + + private int order; + private String key; + + MinecraftVersion( String key, int v ) { + this.key = key; + order = v; + } + + // Would be really cool if we could overload operators here + public boolean greaterThanOrEqualTo( MinecraftVersion other ) { + return order >= other.order; + } + + public boolean lessThanOrEqualTo( MinecraftVersion other ) { + return order <= other.order; + } + + public static MinecraftVersion get( String v ) { + for ( MinecraftVersion k : MinecraftVersion.values() ) { + if ( v.contains( k.key ) ) { + return k; + } + } + return null; + } + } +} diff --git a/src/me/rockyhawk/commandpanels/openpanelsmanager/OpenGUI.java b/src/me/rockyhawk/commandpanels/openpanelsmanager/OpenGUI.java index 3ff38a8..37c8581 100644 --- a/src/me/rockyhawk/commandpanels/openpanelsmanager/OpenGUI.java +++ b/src/me/rockyhawk/commandpanels/openpanelsmanager/OpenGUI.java @@ -1,6 +1,7 @@ package me.rockyhawk.commandpanels.openpanelsmanager; import me.rockyhawk.commandpanels.CommandPanels; +import me.rockyhawk.commandpanels.ioclasses.NBTEditor; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; @@ -136,6 +137,7 @@ public class OpenGUI { id = Short.parseShort(pconfig.getString("emptyID")); } empty = new ItemStack(Objects.requireNonNull(Material.matchMaterial(Objects.requireNonNull(pconfig.getString("empty")).toUpperCase())), 1,id); + empty = NBTEditor.set(empty,"CommandPanels","plugin"); if (empty.getType() == Material.AIR) { continue; } diff --git a/src/me/rockyhawk/commandpanels/openwithitem/HotbarItemLoader.java b/src/me/rockyhawk/commandpanels/openwithitem/HotbarItemLoader.java index a3e5787..83741bf 100644 --- a/src/me/rockyhawk/commandpanels/openwithitem/HotbarItemLoader.java +++ b/src/me/rockyhawk/commandpanels/openwithitem/HotbarItemLoader.java @@ -73,7 +73,7 @@ public class HotbarItemLoader { } } if(tempFile.contains("open-with-item")){ - ItemStack panelItem = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(tempFile.getConfigurationSection("open-with-item")), p, false, true, true); + ItemStack panelItem = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(tempFile.getConfigurationSection("open-with-item")), p, false, true, false); if(invItem != null && panelItem != null) { panelItem.setAmount(invItem.getAmount()); }else{ diff --git a/src/me/rockyhawk/commandpanels/openwithitem/UtilsOpenWithItem.java b/src/me/rockyhawk/commandpanels/openwithitem/UtilsOpenWithItem.java index 13aea2d..4b2d076 100644 --- a/src/me/rockyhawk/commandpanels/openwithitem/UtilsOpenWithItem.java +++ b/src/me/rockyhawk/commandpanels/openwithitem/UtilsOpenWithItem.java @@ -111,7 +111,7 @@ public class UtilsOpenWithItem implements Listener { } } if (p.hasPermission("commandpanel.panel." + temp.getString("panels." + key + ".perm")) && temp.contains("panels." + key + ".open-with-item")) { - ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, true); + ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, false); if(temp.contains("panels." + key + ".open-with-item.stationary")) { if (0 <= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))) && 33 >= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary")))) { p.getInventory().setItem(Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))), s); @@ -153,7 +153,7 @@ public class UtilsOpenWithItem implements Listener { } } if (p.hasPermission("commandpanel.panel." + temp.getString("panels." + key + ".perm")) && temp.contains("panels." + key + ".open-with-item")) { - ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, true); + ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, false); if(temp.contains("panels." + key + ".open-with-item.stationary") && 0 <= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))) && 33 >= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary")))){ p.getInventory().setItem(Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))), s); } @@ -187,7 +187,7 @@ public class UtilsOpenWithItem implements Listener { key = (String) var10.next(); if (p.hasPermission("commandpanel.panel." + temp.getString("panels." + key + ".perm")) && temp.contains("panels." + key + ".open-with-item")) { if(temp.contains("panels." + key + ".open-with-item.stationary")){ - ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, true); + ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, false); e.getDrops().remove(s); } } @@ -225,7 +225,7 @@ public class UtilsOpenWithItem implements Listener { continue; } } - ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, true); + ItemStack s = plugin.itemCreate.makeItemFromConfig(Objects.requireNonNull(temp.getConfigurationSection("panels." + key + ".open-with-item")), p, false, true, false); if(temp.contains("panels." + key + ".open-with-item.stationary") && 0 <= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))) && 33 >= Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary")))){ p.getInventory().setItem(Integer.parseInt(Objects.requireNonNull(temp.getString("panels." + key + ".open-with-item.stationary"))), s); }