Merge pull request #1 from Jowcey/master

New Features + 1.18 Update
This commit is contained in:
PretzelJohn 2021-12-13 15:07:35 -05:00 committed by GitHub
commit 1d680d581d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 4327 additions and 364 deletions

16
pom.xml
View File

@ -29,9 +29,19 @@
<id>spigotmc-repo</id> <id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository> </repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
<layout>default</layout>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId> <artifactId>spigot</artifactId>
@ -43,5 +53,11 @@
<version>1.16.5-R0.1-SNAPSHOT</version> <version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>de.tr7zw</groupId>
<artifactId>functional-annotations</artifactId>
<version>0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -3,7 +3,6 @@ package com.pretzel.dev.villagertradelimiter.listeners;
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter; import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
import com.pretzel.dev.villagertradelimiter.lib.Util; import com.pretzel.dev.villagertradelimiter.lib.Util;
import com.pretzel.dev.villagertradelimiter.nms.*; import com.pretzel.dev.villagertradelimiter.nms.*;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@ -14,7 +13,6 @@ import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
@ -26,11 +24,9 @@ public class PlayerListener implements Listener {
private static final Material[] MATERIALS = new Material[] { Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.BELL, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.SHIELD, Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.FILLED_MAP, Material.FISHING_ROD, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.LEATHER_HORSE_ARMOR, Material.SADDLE, Material.ENCHANTED_BOOK, Material.STONE_AXE, Material.STONE_SHOVEL, Material.STONE_PICKAXE, Material.STONE_HOE, Material.IRON_AXE, Material.IRON_SHOVEL, Material.IRON_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_PICKAXE, Material.DIAMOND_HOE, Material.IRON_SWORD, Material.DIAMOND_SWORD, Material.NETHERITE_AXE, Material.NETHERITE_HOE, Material.NETHERITE_PICKAXE, Material.NETHERITE_SHOVEL, Material.NETHERITE_SWORD, Material.NETHERITE_HELMET, Material.NETHERITE_CHESTPLATE, Material.NETHERITE_LEGGINGS, Material.NETHERITE_BOOTS }; private static final Material[] MATERIALS = new Material[] { Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.BELL, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.SHIELD, Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.FILLED_MAP, Material.FISHING_ROD, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.LEATHER_HORSE_ARMOR, Material.SADDLE, Material.ENCHANTED_BOOK, Material.STONE_AXE, Material.STONE_SHOVEL, Material.STONE_PICKAXE, Material.STONE_HOE, Material.IRON_AXE, Material.IRON_SHOVEL, Material.IRON_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_PICKAXE, Material.DIAMOND_HOE, Material.IRON_SWORD, Material.DIAMOND_SWORD, Material.NETHERITE_AXE, Material.NETHERITE_HOE, Material.NETHERITE_PICKAXE, Material.NETHERITE_SHOVEL, Material.NETHERITE_SWORD, Material.NETHERITE_HELMET, Material.NETHERITE_CHESTPLATE, Material.NETHERITE_LEGGINGS, Material.NETHERITE_BOOTS };
private final VillagerTradeLimiter instance; private final VillagerTradeLimiter instance;
private final NMS nms;
public PlayerListener(VillagerTradeLimiter instance) { public PlayerListener(VillagerTradeLimiter instance) {
this.instance = instance; this.instance = instance;
this.nms = new NMS(Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]);
} }
@EventHandler @EventHandler
@ -60,10 +56,71 @@ public class PlayerListener implements Listener {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if(Util.isNPC(player)) return; //Skips NPCs if(Util.isNPC(player)) return; //Skips NPCs
this.hotv(player); this.hotv(player);
this.setIngredients(villager);
this.setData(villager);
this.maxDiscount(villager, player); this.maxDiscount(villager, player);
this.maxDemand(villager); this.maxDemand(villager);
} }
private void setIngredients(final Villager villager) {
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
final NBTEntity villagerNBT = new NBTEntity(villager);
NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
for (NBTCompound recipe : recipes) {
if(overrides != null) {
for(final String override : overrides.getKeys(false)) {
final ConfigurationSection item = this.getItem(recipe, override);
if(item != null) {
if (item.contains("item-1-material"))
recipe.getCompound("buy").setString("id", "minecraft:" + item.getString("item-1-material"));
if (item.contains("item-2-material"))
recipe.getCompound("buyB").setString("id", "minecraft:" + item.getString("item-2-material"));
if (recipe.getCompound("buy").getString("id") != "minecraft:air" && item.contains("item-1-amount")) {
int cost = item.getInt("item-1-amount");
if (cost <= 0)
cost = 1;
else if (cost > 64)
cost = 64;
recipe.getCompound("buy").setInteger("Count", cost);
}
if (recipe.getCompound("buyB").getString("id") != "minecraft:air" && item.contains("item-2-amount")) {
int cost2 = item.getInt("item-2-amount");
if (cost2 <= 0)
cost2 = 1;
else if (cost2 > 64)
cost2 = 64;
recipe.getCompound("buyB").setInteger("Count", cost2);
}
break;
}
}
}
}
}
private void setData(final Villager villager) {
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
final NBTEntity villagerNBT = new NBTEntity(villager);
NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
for (NBTCompound recipe : recipes) {
if(overrides != null) {
for(final String override : overrides.getKeys(false)) {
final ConfigurationSection item = this.getItem(recipe, override);
if(item != null) {
if (item.contains("uses")) {
int uses = item.getInt("uses");
if (uses > 0)
recipe.setInteger("maxUses", uses);
}
break;
}
}
}
}
}
//Hero of the Village effect limiter feature //Hero of the Village effect limiter feature
private void hotv(final Player player) { private void hotv(final Player player) {
final PotionEffectType effect = PotionEffectType.HERO_OF_THE_VILLAGE; final PotionEffectType effect = PotionEffectType.HERO_OF_THE_VILLAGE;
@ -82,43 +139,44 @@ public class PlayerListener implements Listener {
//MaxDiscount feature - limits the lowest discounted price to a % of the base price //MaxDiscount feature - limits the lowest discounted price to a % of the base price
private void maxDiscount(final Villager villager, final Player player) { private void maxDiscount(final Villager villager, final Player player) {
final List<MerchantRecipe> recipes = villager.getRecipes(); int majorPositiveValue = 0, minorPositiveValue = 0, tradingValue = 0, minorNegativeValue = 0, majorNegativeValue = 0;
int a = 0, b = 0, c = 0, d = 0, e = 0;
final NBTContainer vnbt = new NBTContainer(this.nms, villager); NBTEntity nbtEntity = new NBTEntity(villager);
final NBTTagList gossips = new NBTTagList(this.nms, vnbt.getTag().get("Gossips")); final NBTEntity playerNBT = new NBTEntity(player);
final NBTContainer pnbt = new NBTContainer(this.nms, player); final String playerUUID = Util.intArrayToString(playerNBT.getIntArray("UUID"));
final String puuid = Util.intArrayToString(pnbt.getTag().getIntArray("UUID")); if (nbtEntity.hasKey("Gossips")) {
NBTCompoundList gossips = nbtEntity.getCompoundList("Gossips");
for (int i = 0; i < gossips.size(); ++i) { for (NBTCompound gossip : gossips) {
final NBTTagCompound gossip = gossips.getCompound(i); final String type = gossip.getString("Type");
final String type = gossip.getString("Type"); final String targetUUID = Util.intArrayToString(gossip.getIntArray("Target"));
final String tuuid = Util.intArrayToString(gossip.getIntArray("Target")); final int value = gossip.getInteger("Value");
final int value = gossip.getInt("Value"); if (targetUUID == playerUUID) {
if (tuuid.equals(puuid)) { switch (type) {
switch(type) { case "trading": tradingValue = value; break;
case "trading": c = value; break; case "minor_positive": minorPositiveValue = value; break;
case "minor_positive": b = value; break; case "minor_negative": minorNegativeValue = value; break;
case "minor_negative": d = value; break; case "major_positive": majorPositiveValue = value; break;
case "major_positive": a = value; break; case "major_negative": majorNegativeValue = value; break;
case "major_negative": e = value; break; default: break;
default: break; }
} }
} }
} }
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides"); final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
final ArrayList<MerchantRecipe> finalRecipes = new ArrayList<>(); final NBTEntity villagerNBT = new NBTEntity(villager);
for (final MerchantRecipe recipe : recipes) { NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
final int x = recipe.getIngredients().get(0).getAmount(); List<NBTCompound> remove = new ArrayList<>();
final float p0 = this.getPriceMultiplier(recipe); for (NBTCompound recipe : recipes) {
final int w = 5 * a + b + c - d - 5 * e; final int ingredientAmount = recipe.getCompound("buy").getInteger("Count");
final float y = x - p0 * w; final float priceMultiplier = this.getPriceMultiplier(recipe);
final int valueModifier = 5 * majorPositiveValue + minorPositiveValue + tradingValue - minorNegativeValue - 5 * majorNegativeValue;
final float finalValue = ingredientAmount - priceMultiplier * valueModifier;
boolean disabled = false; boolean disabled = false;
double maxDiscount = instance.getCfg().getDouble("MaxDiscount", 0.3); double maxDiscount = instance.getCfg().getDouble("MaxDiscount", 0.3);
if(overrides != null) { if(overrides != null) {
for(final String k : overrides.getKeys(false)) { for(final String override : overrides.getKeys(false)) {
final ConfigurationSection item = this.getItem(recipe, k); final ConfigurationSection item = this.getItem(recipe, override);
if(item != null) { if(item != null) {
disabled = item.getBoolean("Disabled", false); disabled = item.getBoolean("Disabled", false);
maxDiscount = item.getDouble("MaxDiscount", maxDiscount); maxDiscount = item.getDouble("MaxDiscount", maxDiscount);
@ -127,53 +185,49 @@ public class PlayerListener implements Listener {
} }
} }
if(maxDiscount >= 0.0 && maxDiscount <= 1.0) { if(maxDiscount >= 0.0 && maxDiscount <= 1.0) {
if(y < x * (1.0 - maxDiscount) && y != x) { if(finalValue < ingredientAmount * (1.0 - maxDiscount) && finalValue != ingredientAmount) {
recipe.setPriceMultiplier(x * (float)maxDiscount / w); recipe.setFloat("priceMultiplier", ingredientAmount * (float)maxDiscount / valueModifier);
} else { } else {
recipe.setPriceMultiplier(p0); recipe.setFloat("priceMultiplier", priceMultiplier);
} }
} else { } else {
recipe.setPriceMultiplier(p0); recipe.setFloat("priceMultiplier", priceMultiplier);
} }
if(!disabled) finalRecipes.add(recipe); if(disabled)
remove.add(recipe);
} }
villager.setRecipes(finalRecipes); remove.forEach(rem -> { recipes.remove(rem); });
Bukkit.getScheduler().runTaskLater(instance, () -> villager.setRecipes(recipes), 0);
} }
//MaxDemand feature - limits demand-based price increases //MaxDemand feature - limits demand-based price increases
private void maxDemand(final Villager villager) { private void maxDemand(final Villager villager) {
List<MerchantRecipe> recipes = villager.getRecipes(); final NBTEntity villagerNBT = new NBTEntity(villager);
final NBTContainer vnbt = new NBTContainer(this.nms, villager);
final NBTTagCompound vtag = vnbt.getTag();
final NBTTagList recipes2 = new NBTTagList(this.nms, vtag.getCompound("Offers").get("Recipes"));
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides"); final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
for(int i = 0; i < recipes2.size(); ++i) { if (villagerNBT.hasKey("Offers")) {
final NBTTagCompound recipe2 = recipes2.getCompound(i); NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
final int demand = recipe2.getInt("demand"); for (NBTCompound recipe : recipes) {
int maxDemand = instance.getCfg().getInt("MaxDemand", -1); final int demand = recipe.getInteger("demand");
if(overrides != null) { int maxDemand = instance.getCfg().getInt("MaxDemand", -1);
for(final String k : overrides.getKeys(false)) { if (overrides != null) {
final ConfigurationSection item = this.getItem(recipes.get(i), k); for (String override : overrides.getKeys(false)) {
if(item != null) { final ConfigurationSection item = this.getItem(recipe, override);
maxDemand = item.getInt("MaxDemand", maxDemand); if(item != null) {
break; maxDemand = item.getInt("MaxDemand", maxDemand);
break;
}
} }
} }
} if(maxDemand >= 0 && demand > maxDemand) {
if(maxDemand >= 0 && demand > maxDemand) { recipe.setInteger("demand", maxDemand);
recipe2.setInt("demand", maxDemand); }
} }
} }
villager.getInventory().clear();
vnbt.saveTag(villager, vtag);
} }
//Returns the price multiplier for a given trade //Returns the price multiplier for a given trade
private float getPriceMultiplier(final MerchantRecipe recipe) { private float getPriceMultiplier(final NBTCompound recipe) {
float p = 0.05f; float p = 0.05f;
final Material type = recipe.getResult().getType(); final Material type = recipe.getItemStack("sell").getType();
for(int length = MATERIALS.length, i = 0; i < length; ++i) { for(int length = MATERIALS.length, i = 0; i < length; ++i) {
if(type == MATERIALS[i]) { if(type == MATERIALS[i]) {
p = 0.2f; p = 0.2f;
@ -184,7 +238,7 @@ public class PlayerListener implements Listener {
} }
//Returns the configured settings for a trade //Returns the configured settings for a trade
private ConfigurationSection getItem(final MerchantRecipe recipe, final String k) { private ConfigurationSection getItem(final NBTCompound recipe, final String k) {
final ConfigurationSection item = instance.getCfg().getConfigurationSection("Overrides."+k); final ConfigurationSection item = instance.getCfg().getConfigurationSection("Overrides."+k);
if(item == null) return null; if(item == null) return null;
@ -198,25 +252,26 @@ public class PlayerListener implements Listener {
try { try {
//Return the enchanted book item if there's a number in the item name //Return the enchanted book item if there's a number in the item name
final int level = Integer.parseInt(words[words.length-1]); final int level = Integer.parseInt(words[words.length-1]);
if(recipe.getResult().getType() != Material.ENCHANTED_BOOK) return null; if(recipe.getItemStack("sell").getType() == Material.ENCHANTED_BOOK) {
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta)recipe.getResult().getItemMeta(); final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) recipe.getItemStack("sell").getItemMeta();
final Enchantment enchantment = EnchantmentWrapper.getByKey(NamespacedKey.minecraft(k.substring(0, k.lastIndexOf("_")))); final Enchantment enchantment = EnchantmentWrapper.getByKey(NamespacedKey.minecraft(k.substring(0, k.lastIndexOf("_"))));
if(meta == null || enchantment == null) return null; if (meta == null || enchantment == null) return null;
if(meta.hasStoredEnchant(enchantment) && meta.getStoredEnchantLevel(enchantment) == level) return item; if (meta.hasStoredEnchant(enchantment) && meta.getStoredEnchantLevel(enchantment) == level) return item;
return null; }
} catch(NumberFormatException e) { } catch(NumberFormatException e) {
//Return the item if the item name is valid //Return the item if the item name is valid
if(this.verify(recipe, Material.matchMaterial(k))) return item; if(this.verify(recipe, Material.matchMaterial(k)))
return item;
return null; return null;
} catch(Exception e2) { } catch(Exception e2) {
//Send an error message //Send an error message
Util.errorMsg(e2); Util.errorMsg(e2);
return null;
} }
return null;
} }
//Verifies that an item exists in the villager's trade //Verifies that an item exists in the villager's trade
private boolean verify(final MerchantRecipe recipe, final Material material) { private boolean verify(final NBTCompound recipe, final Material material) {
return ((recipe.getResult().getType() == material) || (recipe.getIngredients().get(0).getType() == material)); return ((recipe.getItemStack("sell").getType() == material) || (recipe.getItemStack("buy").getType() == material));
} }
} }

View File

@ -1,23 +0,0 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util;
import org.bukkit.entity.Entity;
public class CraftEntity {
private final NMS nms;
private final Class<?> c;
public CraftEntity(final NMS nms) {
this.nms = nms;
this.c = nms.getCraftBukkitClass("entity.CraftEntity");
}
public Object getHandle(final Entity entity) {
try {
return nms.getMethod(this.c, "getHandle").invoke(this.c.cast(entity));
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
}

View File

@ -0,0 +1,839 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.bukkit.inventory.ItemStack;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Base class representing NMS Compounds. For a standalone implementation check
* {@link NBTContainer}
*
* @author tr7zw
*
*/
public class NBTCompound {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
private String compundName;
private NBTCompound parent;
protected NBTCompound(NBTCompound owner, String name) {
this.compundName = name;
this.parent = owner;
}
protected Lock getReadLock() {
return readLock;
}
protected Lock getWriteLock() {
return writeLock;
}
protected void saveCompound() {
if (parent != null)
parent.saveCompound();
}
/**
* @return The Compound name
*/
public String getName() {
return compundName;
}
/**
* @return The NMS Compound behind this Object
*/
public Object getCompound() {
return parent.getCompound();
}
protected void setCompound(Object compound) {
parent.setCompound(compound);
}
/**
* @return The parent Compound
*/
public NBTCompound getParent() {
return parent;
}
/**
* Merges all data from comp into this compound. This is done in one action, so
* it also works with Tiles/Entities
*
* @param comp
*/
public void mergeCompound(NBTCompound comp) {
try {
writeLock.lock();
NBTReflectionUtil.mergeOtherNBTCompound(this, comp);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setString(String key, String value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_STRING, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public String getString(String key) {
try {
readLock.lock();
return (String) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_STRING, key);
} finally {
readLock.unlock();
}
}
protected String getContent(String key) {
return NBTReflectionUtil.getContent(this, key);
}
/**
* Setter
*
* @param key
* @param value
*/
public void setInteger(String key, Integer value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_INT, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Integer getInteger(String key) {
try {
readLock.lock();
return (Integer) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_INT, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setDouble(String key, Double value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_DOUBLE, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Double getDouble(String key) {
try {
readLock.lock();
return (Double) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_DOUBLE, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setByte(String key, Byte value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BYTE, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Byte getByte(String key) {
try {
readLock.lock();
return (Byte) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BYTE, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setShort(String key, Short value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_SHORT, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Short getShort(String key) {
try {
readLock.lock();
return (Short) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_SHORT, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setLong(String key, Long value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_LONG, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Long getLong(String key) {
try {
readLock.lock();
return (Long) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_LONG, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setFloat(String key, Float value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_FLOAT, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Float getFloat(String key) {
try {
readLock.lock();
return (Float) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_FLOAT, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setByteArray(String key, byte[] value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BYTEARRAY, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public byte[] getByteArray(String key) {
try {
readLock.lock();
return (byte[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BYTEARRAY, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setIntArray(String key, int[] value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_INTARRAY, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public int[] getIntArray(String key) {
try {
readLock.lock();
return (int[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_INTARRAY, key);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setBoolean(String key, Boolean value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BOOLEAN, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
protected void set(String key, Object val) {
NBTReflectionUtil.set(this, key, val);
saveCompound();
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public Boolean getBoolean(String key) {
try {
readLock.lock();
return (Boolean) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BOOLEAN, key);
} finally {
readLock.unlock();
}
}
/**
* Uses Gson to store an {@link Serializable} Object
*
* @param key
* @param value
*/
public void setObject(String key, Object value) {
try {
writeLock.lock();
NBTReflectionUtil.setObject(this, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Uses Gson to retrieve a stored Object
*
* @param key
* @param type Class of the Object
* @return The created Object or null if empty
*/
public <T> T getObject(String key, Class<T> type) {
try {
readLock.lock();
return NBTReflectionUtil.getObject(this, key, type);
} finally {
readLock.unlock();
}
}
/**
* Save an ItemStack as a compound under a given key
*
* @param key
* @param item
*/
public void setItemStack(String key, ItemStack item) {
try {
writeLock.lock();
removeKey(key);
addCompound(key).mergeCompound(NBTItem.convertItemtoNBT(item));
} finally {
writeLock.unlock();
}
}
/**
* Get an ItemStack that was saved at the given key
*
* @param key
* @return
*/
public ItemStack getItemStack(String key) {
try {
readLock.lock();
NBTCompound comp = getCompound(key);
return NBTItem.convertNBTtoItem(comp);
} finally {
readLock.unlock();
}
}
/**
* Setter
*
* @param key
* @param value
*/
public void setUUID(String key, UUID value) {
try {
writeLock.lock();
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_UUID, key, value);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* Getter
*
* @param key
* @return The stored value or NMS fallback
*/
public UUID getUUID(String key) {
try {
readLock.lock();
return (UUID) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_UUID, key);
} finally {
readLock.unlock();
}
}
/**
* @param key
* @return True if the key is set
*/
public Boolean hasKey(String key) {
try {
readLock.lock();
Boolean b = (Boolean) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_HAS_KEY, key);
if (b == null)
return false;
return b;
} finally {
readLock.unlock();
}
}
/**
* @param key Deletes the given Key
*/
public void removeKey(String key) {
try {
writeLock.lock();
NBTReflectionUtil.remove(this, key);
saveCompound();
} finally {
writeLock.unlock();
}
}
/**
* @return Set of all stored Keys
*/
public Set<String> getKeys() {
try {
readLock.lock();
return NBTReflectionUtil.getKeys(this);
} finally {
readLock.unlock();
}
}
/**
* Creates a subCompound, or returns it if already provided
*
* @param name Key to use
* @return The subCompound Object
*/
public NBTCompound addCompound(String name) {
try {
writeLock.lock();
if (getType(name) == NBTType.NBTTagCompound)
return getCompound(name);
NBTReflectionUtil.addNBTTagCompound(this, name);
NBTCompound comp = getCompound(name);
if (comp == null)
throw new NbtApiException("Error while adding Compound, got null!");
saveCompound();
return comp;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The Compound instance or null
*/
public NBTCompound getCompound(String name) {
try {
readLock.lock();
if (getType(name) != NBTType.NBTTagCompound)
return null;
NBTCompound next = new NBTCompound(this, name);
if (NBTReflectionUtil.valideCompound(next))
return next;
return null;
} finally {
readLock.unlock();
}
}
/**
* The same as addCompound, just with a name that better reflects what it does
*
* @param name
* @return
*/
public NBTCompound getOrCreateCompound(String name) {
return addCompound(name);
}
/**
* @param name
* @return The retrieved String List
*/
public NBTList<String> getStringList(String name) {
try {
writeLock.lock();
NBTList<String> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagString, String.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The retrieved Integer List
*/
public NBTList<Integer> getIntegerList(String name) {
try {
writeLock.lock();
NBTList<Integer> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagInt, Integer.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The retrieved Float List
*/
public NBTList<Float> getFloatList(String name) {
try {
writeLock.lock();
NBTList<Float> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagFloat, Float.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The retrieved Double List
*/
public NBTList<Double> getDoubleList(String name) {
try {
writeLock.lock();
NBTList<Double> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagDouble, Double.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The retrieved Long List
*/
public NBTList<Long> getLongList(String name) {
try {
writeLock.lock();
NBTList<Long> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagLong, Long.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* Returns the type of the list, null if not a list
*
* @param name
* @return
*/
public NBTType getListType(String name) {
try {
readLock.lock();
if (getType(name) != NBTType.NBTTagList)
return null;
return NBTReflectionUtil.getListType(this, name);
} finally {
readLock.unlock();
}
}
/**
* @param name
* @return The retrieved Compound List
*/
public NBTCompoundList getCompoundList(String name) {
try {
writeLock.lock();
NBTCompoundList list = (NBTCompoundList) NBTReflectionUtil.getList(this, name, NBTType.NBTTagCompound,
NBTListCompound.class);
saveCompound();
return list;
} finally {
writeLock.unlock();
}
}
/**
* @param name
* @return The type of the given stored key or null
*/
public NBTType getType(String name) {
try {
readLock.lock();
if (MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
Object nbtbase = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
if(nbtbase == null)
return null;
return NBTType.valueOf((byte)ReflectionMethod.COMPOUND_OWN_TYPE.run(nbtbase));
}
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_TYPE, name);
if (o == null)
return null;
return NBTType.valueOf((byte) o);
} finally {
readLock.unlock();
}
}
public void writeCompound(OutputStream stream) {
try {
writeLock.lock();
NBTReflectionUtil.writeApiNBT(this, stream);
} finally {
writeLock.unlock();
}
}
@Override
public String toString() {
/*
* StringBuilder result = new StringBuilder(); for (String key : getKeys()) {
* result.append(toString(key)); } return result.toString();
*/
return asNBTString();
}
/**
* @deprecated Just use toString()
* @param key
* @return A string representation of the given key
*/
@Deprecated
public String toString(String key) {
/*
* StringBuilder result = new StringBuilder(); NBTCompound compound = this;
* while (compound.getParent() != null) { result.append(" "); compound =
* compound.getParent(); } if (this.getType(key) == NBTType.NBTTagCompound) {
* return this.getCompound(key).toString(); } else { return result + "-" + key +
* ": " + getContent(key) + System.lineSeparator(); }
*/
return asNBTString();
}
/**
* @deprecated Just use toString()
* @return A {@link String} representation of the NBT in Mojang JSON. This is different from normal JSON!
*/
@Deprecated
public String asNBTString() {
try {
readLock.lock();
Object comp = NBTReflectionUtil.gettoCompount(getCompound(), this);
if (comp == null)
return "{}";
return comp.toString();
} finally {
readLock.unlock();
}
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Does a deep compare to check if everything is the same
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if(obj instanceof NBTCompound) {
NBTCompound other = (NBTCompound) obj;
if(getKeys().equals(other.getKeys())) {
for(String key : getKeys()) {
if(!isEqual(this, other, key)) {
return false;
}
}
return true;
}
}
return false;
}
protected static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
if(compA.getType(key) != compB.getType(key))return false;
switch(compA.getType(key)) {
case NBTTagByte:
return compA.getByte(key).equals(compB.getByte(key));
case NBTTagByteArray:
return Arrays.equals(compA.getByteArray(key), compB.getByteArray(key));
case NBTTagCompound:
return compA.getCompound(key).equals(compB.getCompound(key));
case NBTTagDouble:
return compA.getDouble(key).equals(compB.getDouble(key));
case NBTTagEnd:
return true; //??
case NBTTagFloat:
return compA.getFloat(key).equals(compB.getFloat(key));
case NBTTagInt:
return compA.getInteger(key).equals(compB.getInteger(key));
case NBTTagIntArray:
return Arrays.equals(compA.getIntArray(key), compB.getIntArray(key));
case NBTTagList:
return NBTReflectionUtil.getEntry(compA, key).toString().equals(NBTReflectionUtil.getEntry(compB, key).toString()); // Just string compare the 2 lists
case NBTTagLong:
return compA.getLong(key).equals(compB.getLong(key));
case NBTTagShort:
return compA.getShort(key).equals(compB.getShort(key));
case NBTTagString:
return compA.getString(key).equals(compB.getString(key));
}
return false;
}
}

View File

@ -0,0 +1,106 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* {@link NBTListCompound} implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTCompoundList extends NBTList<NBTListCompound> {
protected NBTCompoundList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
/**
* Adds a new Compound to the end of the List and returns it.
*
* @return The added {@link NBTListCompound}
*/
public NBTListCompound addCompound() {
return (NBTListCompound) addCompound(null);
}
/**
* Adds a copy of the Compound to the end of the List and returns it.
* When null is given, a new Compound will be created
*
* @param comp
* @return
*/
public NBTCompound addCompound(NBTCompound comp) {
try {
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
ReflectionMethod.LIST_ADD.run(listObject, size(), compound);
} else {
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, compound);
}
getParent().saveCompound();
NBTListCompound listcomp = new NBTListCompound(this, compound);
if(comp != null){
listcomp.mergeCompound(comp);
}
return listcomp;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
/**
* Adds a new Compound to the end of the List.
*
*
* @deprecated Please use addCompound!
* @param empty
* @return True, if compound was added
*/
@Override
@Deprecated
public boolean add(NBTListCompound empty) {
return addCompound(empty) != null;
}
@Override
public void add(int index, NBTListCompound element) {
if (element != null) {
throw new NbtApiException("You need to pass null! ListCompounds from other lists won't work.");
}
try {
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
ReflectionMethod.LIST_ADD.run(listObject, index, compound);
} else {
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, compound);
}
super.getParent().saveCompound();
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
@Override
public NBTListCompound get(int index) {
try {
Object compound = ReflectionMethod.LIST_GET_COMPOUND.run(listObject, index);
return new NBTListCompound(this, compound);
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
@Override
public NBTListCompound set(int index, NBTListCompound element) {
throw new NbtApiException("This method doesn't work in the ListCompound context.");
}
@Override
protected Object asTag(NBTListCompound object) {
return null;
}
}

View File

@ -1,44 +1,82 @@
package com.pretzel.dev.villagertradelimiter.nms; package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util; import java.io.InputStream;
import org.bukkit.entity.Entity;
public class NBTContainer { import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
private final NMS nms; import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
private final Entity entity; import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
private final NBTTagCompound tag;
public NBTContainer(final NMS nms, final Entity entity) { /**
this.nms = nms; * A Standalone {@link NBTCompound} implementation. All data is just kept inside
this.entity = entity; * this Object.
this.tag = this.loadTag(); *
} * @author tr7zw
*
*/
public class NBTContainer extends NBTCompound {
public NBTTagCompound loadTag() { private Object nbt;
final CraftEntity craftEntity = new CraftEntity(nms);
final NMSEntity nmsEntity = new NMSEntity(nms);
final Class<?> tgc;
if(nms.getVersion().compareTo("v1_17_R1") < 0)
tgc = nms.getNMSClass("server."+nms.getVersion()+".NBTTagCompound");
else
tgc = nms.getNMSClass("nbt.NBTTagCompound");
try {
final NBTTagCompound tag = new NBTTagCompound(nms, tgc.getDeclaredConstructor().newInstance());
nmsEntity.save(craftEntity.getHandle(this.entity), tag);
return tag;
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public void saveTag(final Entity entity, final NBTTagCompound tag) { /**
final CraftEntity craftEntity = new CraftEntity(nms); * Creates an empty, standalone NBTCompound
final NMSEntity nmsEntity = new NMSEntity(nms); */
nmsEntity.load(craftEntity.getHandle(entity), tag); public NBTContainer() {
} super(null, null);
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
/**
* Takes in any NMS Compound to wrap it
*
* @param nbt
*/
public NBTContainer(Object nbt) {
super(null, null);
if (nbt == null) {
throw new NullPointerException("The NBT-Object can't be null!");
}
if (!ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().isAssignableFrom(nbt.getClass())) {
throw new NbtApiException("The object '" + nbt.getClass() + "' is not a valid NBT-Object!");
}
this.nbt = nbt;
}
/**
* Reads in a NBT InputStream
*
* @param inputsteam
*/
public NBTContainer(InputStream inputsteam) {
super(null, null);
this.nbt = NBTReflectionUtil.readNBT(inputsteam);
}
/**
* Parses in a NBT String to a standalone {@link NBTCompound}. Can throw a
* {@link NbtApiException} in case something goes wrong.
*
* @param nbtString
*/
public NBTContainer(String nbtString) {
super(null, null);
if (nbtString == null) {
throw new NullPointerException("The String can't be null!");
}
try {
nbt = ReflectionMethod.PARSE_NBT.run(null, nbtString);
} catch (Exception ex) {
throw new NbtApiException("Unable to parse Malformed Json!", ex);
}
}
@Override
public Object getCompound() {
return nbt;
}
@Override
public void setCompound(Object tag) {
nbt = tag;
}
public NBTTagCompound getTag() {
return this.tag;
}
} }

View File

@ -0,0 +1,45 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Double implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTDoubleList extends NBTList<Double> {
protected NBTDoubleList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
@Override
protected Object asTag(Double object) {
try {
Constructor<?> con = ClassWrapper.NMS_NBTTAGDOUBLE.getClazz().getDeclaredConstructor(double.class);
con.setAccessible(true);
return con.newInstance(object);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
}
}
@Override
public Double get(int index) {
try {
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
return Double.valueOf(obj.toString());
} catch (NumberFormatException nf) {
return 0d;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
}

View File

@ -0,0 +1,54 @@
package com.pretzel.dev.villagertradelimiter.nms;
import org.bukkit.entity.Entity;
import de.tr7zw.annotations.FAUtil;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.AvailableSince;
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.CheckUtil;
/**
* NBT class to access vanilla tags from Entities. Entities don't support custom
* tags. Use the NBTInjector for custom tags. Changes will be instantly applied
* to the Entity, use the merge method to do many things at once.
*
* @author tr7zw
*
*/
public class NBTEntity extends NBTCompound {
private final Entity ent;
/**
* @param entity Any valid Bukkit Entity
*/
public NBTEntity(Entity entity) {
super(null, null);
if (entity == null) {
throw new NullPointerException("Entity can't be null!");
}
ent = entity;
}
@Override
public Object getCompound() {
return NBTReflectionUtil.getEntityNBTTagCompound(NBTReflectionUtil.getNMSEntity(ent));
}
@Override
protected void setCompound(Object compound) {
NBTReflectionUtil.setEntityNBTTag(compound, NBTReflectionUtil.getNMSEntity(ent));
}
/**
* Gets the NBTCompound used by spigots PersistentDataAPI. This method is only
* available for 1.14+!
*
* @return NBTCompound containing the data of the PersistentDataAPI
*/
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
return new NBTPersistentDataContainer(ent.getPersistentDataContainer());
}
}

View File

@ -0,0 +1,80 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
/**
* {@link NBTCompound} implementation backed by a {@link File}
*
* @author tr7zw
*
*/
public class NBTFile extends NBTCompound {
private final File file;
private Object nbt;
/**
* Creates a NBTFile that uses @param file to store it's data. If this file
* exists, the data will be loaded.
*
* @param file
* @throws IOException
*/
public NBTFile(File file) throws IOException {
super(null, null);
if (file == null) {
throw new NullPointerException("File can't be null!");
}
this.file = file;
if (file.exists()) {
FileInputStream inputsteam = new FileInputStream(file);
nbt = NBTReflectionUtil.readNBT(inputsteam);
} else {
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
save();
}
}
/**
* Saves the data to the file
*
* @throws IOException
*/
public void save() throws IOException {
try {
getWriteLock().lock();
if (!file.exists()) {
file.getParentFile().mkdirs();
if (!file.createNewFile())
throw new IOException("Unable to create file at " + file.getAbsolutePath());
}
FileOutputStream outStream = new FileOutputStream(file);
NBTReflectionUtil.writeNBT(nbt, outStream);
} finally {
getWriteLock().unlock();
}
}
/**
* @return The File used to store the data
*/
public File getFile() {
return file;
}
@Override
public Object getCompound() {
return nbt;
}
@Override
protected void setCompound(Object compound) {
nbt = compound;
}
}

View File

@ -0,0 +1,45 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Float implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTFloatList extends NBTList<Float> {
protected NBTFloatList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
@Override
protected Object asTag(Float object) {
try {
Constructor<?> con = ClassWrapper.NMS_NBTTAGFLOAT.getClazz().getDeclaredConstructor(float.class);
con.setAccessible(true);
return con.newInstance(object);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
}
}
@Override
public Float get(int index) {
try {
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
return Float.valueOf(obj.toString());
} catch (NumberFormatException nf) {
return 0f;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
}

View File

@ -0,0 +1,45 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Integer implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTIntegerList extends NBTList<Integer> {
protected NBTIntegerList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
@Override
protected Object asTag(Integer object) {
try {
Constructor<?> con = ClassWrapper.NMS_NBTTAGINT.getClazz().getDeclaredConstructor(int.class);
con.setAccessible(true);
return con.newInstance(object);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
}
}
@Override
public Integer get(int index) {
try {
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
return Integer.valueOf(obj.toString());
} catch (NumberFormatException nf) {
return 0;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
}

View File

@ -0,0 +1,176 @@
package com.pretzel.dev.villagertradelimiter.nms;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* NBT class to access vanilla/custom tags on ItemStacks. This class doesn't
* autosave to the Itemstack, use getItem to get the changed ItemStack
*
* @author tr7zw
*
*/
public class NBTItem extends NBTCompound {
private ItemStack bukkitItem;
private boolean directApply;
private ItemStack originalSrcStack = null;
/**
* Constructor for NBTItems. The ItemStack will be cloned!
*
* @param item
*/
public NBTItem(ItemStack item) {
this(item, false);
}
/**
* Constructor for NBTItems. The ItemStack will be cloned! If directApply is true,
* all changed will be mapped to the original item. Changes to the NBTItem will overwrite changes done
* to the original item in that case.
*
* @param item
* @param directApply
*/
public NBTItem(ItemStack item, boolean directApply) {
super(null, null);
if (item == null || item.getType() == Material.AIR) {
throw new NullPointerException("ItemStack can't be null/Air!");
}
this.directApply = directApply;
bukkitItem = item.clone();
if(directApply) {
this.originalSrcStack = item;
}
}
@Override
public Object getCompound() {
return NBTReflectionUtil.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
}
@Override
protected void setCompound(Object compound) {
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, compound);
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
}
/**
* Apply stored NBT tags to the provided ItemStack.
* <p>
* Note: This will completely override current item's {@link ItemMeta}.
* If you still want to keep the original item's NBT tags, see
* {@link #mergeNBT(ItemStack)} and {@link #mergeCustomNBT(ItemStack)}.
*
* @param item ItemStack that should get the new NBT data
*/
public void applyNBT(ItemStack item) {
if (item == null || item.getType() == Material.AIR) {
throw new NullPointerException("ItemStack can't be null/Air!");
}
NBTItem nbti = new NBTItem(new ItemStack(item.getType()));
nbti.mergeCompound(this);
item.setItemMeta(nbti.getItem().getItemMeta());
}
/**
* Merge all NBT tags to the provided ItemStack.
*
* @param item ItemStack that should get the new NBT data
*/
public void mergeNBT(ItemStack item) {
NBTItem nbti = new NBTItem(item);
nbti.mergeCompound(this);
item.setItemMeta(nbti.getItem().getItemMeta());
}
/**
* Merge only custom (non-vanilla) NBT tags to the provided ItemStack.
*
* @param item ItemStack that should get the new NBT data
*/
public void mergeCustomNBT(ItemStack item) {
if (item == null || item.getType() == Material.AIR) {
throw new NullPointerException("ItemStack can't be null/Air!");
}
ItemMeta meta = item.getItemMeta();
NBTReflectionUtil.getUnhandledNBTTags(meta).putAll(NBTReflectionUtil.getUnhandledNBTTags(bukkitItem.getItemMeta()));
item.setItemMeta(meta);
}
/**
* True, if the item has any tags now known for this item type.
*
* @return true when custom tags are present
*/
public boolean hasCustomNbtData() {
ItemMeta meta = bukkitItem.getItemMeta();
return !NBTReflectionUtil.getUnhandledNBTTags(meta).isEmpty();
}
/**
* Remove all custom (non-vanilla) NBT tags from the NBTItem.
*/
public void clearCustomNBT() {
ItemMeta meta = bukkitItem.getItemMeta();
NBTReflectionUtil.getUnhandledNBTTags(meta).clear();
bukkitItem.setItemMeta(meta);
}
/**
* @return The modified ItemStack
*/
public ItemStack getItem() {
return bukkitItem;
}
protected void setItem(ItemStack item) {
bukkitItem = item;
}
/**
* This may return true even when the NBT is empty.
*
* @return Does the ItemStack have a NBTCompound.
*/
public boolean hasNBTData() {
return getCompound() != null;
}
/**
* Helper method that converts {@link ItemStack} to {@link NBTContainer} with
* all it's data like Material, Damage, Amount and Tags.
*
* @param item
* @return Standalone {@link NBTContainer} with the Item's data
*/
public static NBTContainer convertItemtoNBT(ItemStack item) {
return NBTReflectionUtil.convertNMSItemtoNBTCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, item));
}
/**
* Helper method to do the inverse to "convertItemtoNBT". Creates an
* {@link ItemStack} using the {@link NBTCompound}
*
* @param comp
* @return ItemStack using the {@link NBTCompound}'s data
*/
public static ItemStack convertNBTtoItem(NBTCompound comp) {
return (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null,
NBTReflectionUtil.convertNBTCompoundtoNMSItem(comp));
}
@Override
protected void saveCompound() {
if(directApply) {
applyNBT(originalSrcStack);
}
}
}

View File

@ -0,0 +1,429 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Abstract List implementation for ListCompounds
*
* @author tr7zw
*
* @param <T>
*/
public abstract class NBTList<T> implements List<T> {
private String listName;
private NBTCompound parent;
private NBTType type;
protected Object listObject;
protected NBTList(NBTCompound owner, String name, NBTType type, Object list) {
parent = owner;
listName = name;
this.type = type;
this.listObject = list;
}
/**
* @return Name of this list-compound
*/
public String getName() {
return listName;
}
/**
* @return The Compound's parent Object
*/
public NBTCompound getParent() {
return parent;
}
protected void save() {
parent.set(listName, listObject);
}
protected abstract Object asTag(T object);
@Override
public boolean add(T element) {
try {
parent.getWriteLock().lock();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
ReflectionMethod.LIST_ADD.run(listObject, size(), asTag(element));
} else {
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, asTag(element));
}
save();
return true;
} catch (Exception ex) {
throw new NbtApiException(ex);
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public void add(int index, T element) {
try {
parent.getWriteLock().lock();
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
ReflectionMethod.LIST_ADD.run(listObject, index, asTag(element));
} else {
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, asTag(element));
}
save();
} catch (Exception ex) {
throw new NbtApiException(ex);
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public T set(int index, T element) {
try {
parent.getWriteLock().lock();
T prev = get(index);
ReflectionMethod.LIST_SET.run(listObject, index, asTag(element));
save();
return prev;
} catch (Exception ex) {
throw new NbtApiException(ex);
} finally {
parent.getWriteLock().unlock();
}
}
public T remove(int i) {
try {
parent.getWriteLock().lock();
T old = get(i);
ReflectionMethod.LIST_REMOVE_KEY.run(listObject, i);
save();
return old;
} catch (Exception ex) {
throw new NbtApiException(ex);
} finally {
parent.getWriteLock().unlock();
}
}
public int size() {
try {
parent.getReadLock().lock();
return (int) ReflectionMethod.LIST_SIZE.run(listObject);
} catch (Exception ex) {
throw new NbtApiException(ex);
} finally {
parent.getReadLock().unlock();
}
}
/**
* @return The type that this list contains
*/
public NBTType getType() {
return type;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public void clear() {
while (!isEmpty()) {
remove(0);
}
}
@Override
public boolean contains(Object o) {
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return true;
}
return false;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public int indexOf(Object o) {
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return i;
}
return -1;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public boolean addAll(Collection<? extends T> c) {
try {
parent.getWriteLock().lock();
int size = size();
for (T ele : c) {
add(ele);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
try {
parent.getWriteLock().lock();
int size = size();
for (T ele : c) {
add(index++, ele);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public boolean containsAll(Collection<?> c) {
try {
parent.getReadLock().lock();
for (Object ele : c) {
if (!contains(ele))
return false;
}
return true;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public int lastIndexOf(Object o) {
try {
parent.getReadLock().lock();
int index = -1;
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
index = i;
}
return index;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public boolean removeAll(Collection<?> c) {
try {
parent.getWriteLock().lock();
int size = size();
for (Object obj : c) {
remove(obj);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public boolean retainAll(Collection<?> c) {
try {
parent.getWriteLock().lock();
int size = size();
for (Object obj : c) {
for (int i = 0; i < size(); i++) {
if (!obj.equals(get(i))) {
remove(i--);
}
}
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public boolean remove(Object o) {
try {
parent.getWriteLock().lock();
int size = size();
int id = -1;
while ((id = indexOf(o)) != -1) {
remove(id);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = -1;
@Override
public boolean hasNext() {
return size() > index + 1;
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
return get(++index);
}
@Override
public void remove() {
NBTList.this.remove(index);
index--;
}
};
}
@Override
public ListIterator<T> listIterator() {
return listIterator(0);
}
@Override
public ListIterator<T> listIterator(int startIndex) {
final NBTList<T> list = this;
return new ListIterator<T>() {
int index = startIndex - 1;
@Override
public void add(T e) {
list.add(index, e);
}
@Override
public boolean hasNext() {
return size() > index + 1;
}
@Override
public boolean hasPrevious() {
return index >= 0 && index <= size();
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
return get(++index);
}
@Override
public int nextIndex() {
return index + 1;
}
@Override
public T previous() {
if (!hasPrevious())
throw new NoSuchElementException("Id: " + (index - 1));
return get(index--);
}
@Override
public int previousIndex() {
return index - 1;
}
@Override
public void remove() {
list.remove(index);
index--;
}
@Override
public void set(T e) {
list.set(index, e);
}
};
}
@Override
public Object[] toArray() {
try {
parent.getReadLock().lock();
Object[] ar = new Object[size()];
for (int i = 0; i < size(); i++)
ar[i] = get(i);
return ar;
} finally {
parent.getReadLock().unlock();
}
}
@SuppressWarnings("unchecked")
@Override
public <E> E[] toArray(E[] a) {
try {
parent.getReadLock().lock();
E[] ar = Arrays.copyOf(a, size());
Arrays.fill(ar, null);
Class<?> arrayclass = a.getClass().getComponentType();
for (int i = 0; i < size(); i++) {
T obj = get(i);
if (arrayclass.isInstance(obj)) {
ar[i] = (E) get(i);
} else {
throw new ArrayStoreException("The array does not match the objects stored in the List.");
}
}
return ar;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
try {
parent.getReadLock().lock();
ArrayList<T> list = new ArrayList<>();
for (int i = fromIndex; i < toIndex; i++)
list.add(get(i));
return list;
} finally {
parent.getReadLock().unlock();
}
}
@Override
public String toString() {
try {
parent.getReadLock().lock();
return listObject.toString();
} finally {
parent.getReadLock().unlock();
}
}
}

View File

@ -0,0 +1,42 @@
package com.pretzel.dev.villagertradelimiter.nms;
/**
* Cut down version of the {@link NBTCompound} for inside
* {@link NBTCompoundList} This Compound implementation is missing the ability
* for further subCompounds and Lists. This class probably will change in the
* future
*
* @author tr7zw
*
*/
public class NBTListCompound extends NBTCompound {
private NBTList<?> owner;
private Object compound;
protected NBTListCompound(NBTList<?> parent, Object obj) {
super(null, null);
owner = parent;
compound = obj;
}
public NBTList<?> getListParent() {
return owner;
}
@Override
public Object getCompound() {
return compound;
}
@Override
protected void setCompound(Object compound) {
this.compound = compound;
}
@Override
protected void saveCompound() {
owner.save();
}
}

View File

@ -0,0 +1,45 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Long implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTLongList extends NBTList<Long> {
protected NBTLongList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
@Override
protected Object asTag(Long object) {
try {
Constructor<?> con = ClassWrapper.NMS_NBTTAGLONG.getClazz().getDeclaredConstructor(long.class);
con.setAccessible(true);
return con.newInstance(object);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
}
}
@Override
public Long get(int index) {
try {
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
return Long.valueOf(obj.toString().replace("L", ""));
} catch (NumberFormatException nf) {
return 0l;
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
}

View File

@ -0,0 +1,31 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.util.Map;
import org.bukkit.persistence.PersistentDataContainer;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
public class NBTPersistentDataContainer extends NBTCompound {
private final PersistentDataContainer container;
protected NBTPersistentDataContainer(PersistentDataContainer container) {
super(null, null);
this.container = container;
}
@Override
public Object getCompound() {
return ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_TO_TAG.run(container);
}
@Override
protected void setCompound(Object compound) {
@SuppressWarnings("unchecked")
Map<Object, Object> map = (Map<Object, Object>) ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_GET_MAP.run(container);
map.clear();
ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_PUT_ALL.run(container, compound);
}
}

View File

@ -0,0 +1,625 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.Set;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.meta.ItemMeta;
import com.pretzel.dev.villagertradelimiter.nms.utils.GsonWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* Utility class for translating NBTApi calls to reflections into NMS code All
* methods are allowed to throw {@link NbtApiException}
*
* @author tr7zw
*
*/
public class NBTReflectionUtil {
private static Field field_unhandledTags = null;
static {
try {
field_unhandledTags = ClassWrapper.CRAFT_METAITEM.getClazz().getDeclaredField("unhandledTags");
field_unhandledTags.setAccessible(true);
} catch (NoSuchFieldException e) {
}
}
/**
* Hidden constructor
*/
private NBTReflectionUtil() {
}
/**
* Gets the NMS Entity for a given Bukkit Entity
*
* @param entity Bukkit Entity
* @return NMS Entity
*/
public static Object getNMSEntity(Entity entity) {
try {
return ReflectionMethod.CRAFT_ENTITY_GET_HANDLE.run(ClassWrapper.CRAFT_ENTITY.getClazz().cast(entity));
} catch (Exception e) {
throw new NbtApiException("Exception while getting the NMS Entity from a Bukkit Entity!", e);
}
}
/**
* Reads in a InputStream as NMS Compound
*
* @param stream InputStream of any NBT file
* @return NMS Compound
*/
public static Object readNBT(InputStream stream) {
try {
return ReflectionMethod.NBTFILE_READ.run(null, stream);
} catch (Exception e) {
throw new NbtApiException("Exception while reading a NBT File!", e);
}
}
/**
* Writes a NMS Compound to an OutputStream
*
* @param nbt NMS Compound
* @param stream Stream to write to
* @return ???
*/
public static Object writeNBT(Object nbt, OutputStream stream) {
try {
return ReflectionMethod.NBTFILE_WRITE.run(null, nbt, stream);
} catch (Exception e) {
throw new NbtApiException("Exception while writing NBT!", e);
}
}
/**
* Writes a Compound to an OutputStream
*
* @param comp Compound
* @param stream Stream to write to
*/
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
try {
Object nbttag = comp.getCompound();
if (nbttag == null) {
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return;
Object workingtag = gettoCompount(nbttag, comp);
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
} catch (Exception e) {
throw new NbtApiException("Exception while writing NBT!", e);
}
}
/**
* Simulates getOrCreateTag. If an Item doesn't yet have a Tag, it will return a
* new empty tag.
*
* @param nmsitem
* @return NMS Compound
*/
public static Object getItemRootNBTTagCompound(Object nmsitem) {
try {
Object answer = ReflectionMethod.NMSITEM_GETTAG.run(nmsitem);
return answer != null ? answer : ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
} catch (Exception e) {
throw new NbtApiException("Exception while getting an Itemstack's NBTCompound!", e);
}
}
/**
* Converts {@link NBTCompound} to NMS ItemStacks
*
* @param nbtcompound Any valid {@link NBTCompound}
* @return NMS ItemStack
*/
public static Object convertNBTCompoundtoNMSItem(NBTCompound nbtcompound) {
try {
Object nmsComp = gettoCompount(nbtcompound.getCompound(), nbtcompound);
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_11_R1.getVersionId()) {
return ObjectCreator.NMS_COMPOUNDFROMITEM.getInstance(nmsComp);
} else {
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nmsComp);
}
} catch (Exception e) {
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack!", e);
}
}
/**
* Converts NMS ItemStacks to {@link NBTContainer}
*
* @param nmsitem NMS ItemStack
* @return {@link NBTContainer} with all the data
*/
public static NBTContainer convertNMSItemtoNBTCompound(Object nmsitem) {
try {
Object answer = ReflectionMethod.NMSITEM_SAVE.run(nmsitem, ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance());
return new NBTContainer(answer);
} catch (Exception e) {
throw new NbtApiException("Exception while converting NMS ItemStack to NBTCompound!", e);
}
}
/**
* Gets a live copy of non-vanilla NBT tags.
*
* @param meta ItemMeta from which tags should be retrieved
* @return Map containing unhandled (custom) NBT tags
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> getUnhandledNBTTags(ItemMeta meta) {
try {
return (Map<String, Object>) field_unhandledTags.get(meta);
} catch (Exception e) {
throw new NbtApiException("Exception while getting unhandled tags from ItemMeta!", e);
}
}
/**
* Gets the Vanilla NBT Compound from a given NMS Entity
*
* @param nmsEntity
* @return NMS NBT Compound
*/
public static Object getEntityNBTTagCompound(Object nmsEntity) {
try {
Object nbt = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
Object answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
if (answer == null)
answer = nbt;
return answer;
} catch (Exception e) {
throw new NbtApiException("Exception while getting NBTCompound from NMS Entity!", e);
}
}
/**
* Loads all Vanilla tags from a NMS Compound into a NMS Entity
*
* @param nbtTag
* @param nmsEntity
* @return The NMS Entity
*/
public static Object setEntityNBTTag(Object nbtTag, Object nmsEntity) {
try {
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
return nmsEntity;
} catch (Exception ex) {
throw new NbtApiException("Exception while setting the NBTCompound of an Entity", ex);
}
}
/**
* Gets the NMS Compound from a given TileEntity
*
* @param tile
* @return NMS Compound with the Vanilla data
*/
public static Object getTileEntityNBTTagCompound(BlockState tile) {
try {
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
Object o = null;
if(MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY_1_7_10.run(nmsworld, tile.getX(), tile.getY(), tile.getZ());
}else {
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
}
Object answer = null;
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
answer = ReflectionMethod.TILEENTITY_GET_NBT_1181.run(o);
} else {
answer = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
ReflectionMethod.TILEENTITY_GET_NBT.run(o, answer);
}
if (answer == null) {
throw new NbtApiException("Unable to get NBTCompound from TileEntity! " + tile + " " + o);
}
return answer;
} catch (Exception e) {
throw new NbtApiException("Exception while getting NBTCompound from TileEntity!", e);
}
}
/**
* Sets Vanilla tags from a NMS Compound to a TileEntity
*
* @param tile
* @param comp
*/
public static void setTileEntityNBTTagCompound(BlockState tile, Object comp) {
try {
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
Object o = null;
if(MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY_1_7_10.run(nmsworld, tile.getX(), tile.getY(), tile.getZ());
}else {
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
}
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
ReflectionMethod.TILEENTITY_SET_NBT.run(o, comp);
}else if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
Object blockData = ReflectionMethod.TILEENTITY_GET_BLOCKDATA.run(o);
ReflectionMethod.TILEENTITY_SET_NBT_LEGACY1161.run(o, blockData, comp);
}else {
ReflectionMethod.TILEENTITY_SET_NBT_LEGACY1151.run(o, comp);
}
} catch (Exception e) {
throw new NbtApiException("Exception while setting NBTData for a TileEntity!", e);
}
}
/**
* Gets the subCompound with a given name from a NMS Compound
*
* @param compound
* @param name
* @return NMS Compound or null
*/
public static Object getSubNBTTagCompound(Object compound, String name) {
try {
if ((boolean) ReflectionMethod.COMPOUND_HAS_KEY.run(compound, name)) {
return ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
} else {
throw new NbtApiException("Tried getting invalide compound '" + name + "' from '" + compound + "'!");
}
} catch (Exception e) {
throw new NbtApiException("Exception while getting NBT subcompounds!", e);
}
}
/**
* Creates a subCompound with a given name in the given NMS Compound
*
* @param comp
* @param name
*/
public static void addNBTTagCompound(NBTCompound comp, String name) {
if (name == null) {
remove(comp, name);
return;
}
Object nbttag = comp.getCompound();
if (nbttag == null) {
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp)) {
return;
}
Object workingtag = gettoCompount(nbttag, comp);
try {
ReflectionMethod.COMPOUND_SET.run(workingtag, name,
ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance());
comp.setCompound(nbttag);
} catch (Exception e) {
throw new NbtApiException("Exception while adding a Compound!", e);
}
}
/**
* Checks if the Compound is correctly linked to it's roots
*
* @param comp
* @return true if this is a valide Compound, else false
*/
public static Boolean valideCompound(NBTCompound comp) {
Object root = comp.getCompound();
if (root == null) {
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
return (gettoCompount(root, comp)) != null;
}
protected static Object gettoCompount(Object nbttag, NBTCompound comp) {
Deque<String> structure = new ArrayDeque<>();
while (comp.getParent() != null) {
structure.add(comp.getName());
comp = comp.getParent();
}
while (!structure.isEmpty()) {
String target = structure.pollLast();
nbttag = getSubNBTTagCompound(nbttag, target);
if (nbttag == null) {
throw new NbtApiException("Unable to find tag '" + target + "' in " + nbttag);
}
}
return nbttag;
}
/**
* Merges the second {@link NBTCompound} into the first one
*
* @param comp Target for the merge
* @param nbtcompoundSrc Data to merge
*/
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompoundSrc) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
Object rootnbttagSrc = nbtcompoundSrc.getCompound();
if (rootnbttagSrc == null) {
rootnbttagSrc = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(nbtcompoundSrc))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtagSrc = gettoCompount(rootnbttagSrc, nbtcompoundSrc);
try {
ReflectionMethod.COMPOUND_MERGE.run(workingtag, workingtagSrc);
comp.setCompound(rootnbttag);
} catch (Exception e) {
throw new NbtApiException("Exception while merging two NBTCompounds!", e);
}
}
/**
* Returns the content for a given key inside a Compound
*
* @param comp
* @param key
* @return Content saved under this key
*/
public static String getContent(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
try {
return ReflectionMethod.COMPOUND_GET.run(workingtag, key).toString();
} catch (Exception e) {
throw new NbtApiException("Exception while getting the Content for key '" + key + "'!", e);
}
}
/**
* Sets a key in a {@link NBTCompound} to a given value
*
* @param comp
* @param key
* @param val
*/
public static void set(NBTCompound comp, String key, Object val) {
if (val == null) {
remove(comp, key);
return;
}
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp)) {
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
}
Object workingtag = gettoCompount(rootnbttag, comp);
try {
ReflectionMethod.COMPOUND_SET.run(workingtag, key, val);
comp.setCompound(rootnbttag);
} catch (Exception e) {
throw new NbtApiException("Exception while setting key '" + key + "' to '" + val + "'!", e);
}
}
/**
* Returns the List saved with a given key.
*
* @param comp
* @param key
* @param type
* @param clazz
* @return The list at that key. Null if it's an invalide type
*/
@SuppressWarnings("unchecked")
public static <T> NBTList<T> getList(NBTCompound comp, String key, NBTType type, Class<T> clazz) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
try {
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
if (clazz == String.class) {
return (NBTList<T>) new NBTStringList(comp, key, type, nbt);
} else if (clazz == NBTListCompound.class) {
return (NBTList<T>) new NBTCompoundList(comp, key, type, nbt);
} else if (clazz == Integer.class) {
return (NBTList<T>) new NBTIntegerList(comp, key, type, nbt);
} else if (clazz == Float.class) {
return (NBTList<T>) new NBTFloatList(comp, key, type, nbt);
} else if (clazz == Double.class) {
return (NBTList<T>) new NBTDoubleList(comp, key, type, nbt);
} else if (clazz == Long.class) {
return (NBTList<T>) new NBTLongList(comp, key, type, nbt);
} else {
return null;
}
} catch (Exception ex) {
throw new NbtApiException("Exception while getting a list with the type '" + type + "'!", ex);
}
}
public static NBTType getListType(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
try {
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
String fieldname = "type";
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
fieldname = "w";
}
Field f = nbt.getClass().getDeclaredField(fieldname);
f.setAccessible(true);
return NBTType.valueOf(f.getByte(nbt));
} catch (Exception ex) {
throw new NbtApiException("Exception while getting the list type!", ex);
}
}
public static Object getEntry(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return null;
Object workingtag = gettoCompount(rootnbttag, comp);
try {
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
return nbt;
} catch (Exception ex) {
throw new NbtApiException("Exception while getting an Entry!", ex);
}
}
/**
* Uses Gson to set a {@link Serializable} value in a Compound
*
* @param comp
* @param key
* @param value
*/
public static void setObject(NBTCompound comp, String key, Object value) {
if (!MinecraftVersion.hasGsonSupport())
return;
try {
String json = GsonWrapper.getString(value);
setData(comp, ReflectionMethod.COMPOUND_SET_STRING, key, json);
} catch (Exception e) {
throw new NbtApiException("Exception while setting the Object '" + value + "'!", e);
}
}
/**
* Uses Gson to load back a {@link Serializable} object from the Compound
*
* @param comp
* @param key
* @param type
* @return The loaded Object or null, if not found
*/
public static <T> T getObject(NBTCompound comp, String key, Class<T> type) {
if (!MinecraftVersion.hasGsonSupport())
return null;
String json = (String) getData(comp, ReflectionMethod.COMPOUND_GET_STRING, key);
if (json == null) {
return null;
}
return GsonWrapper.deserializeJson(json, type);
}
/**
* Deletes the given key
*
* @param comp
* @param key
*/
public static void remove(NBTCompound comp, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
return;
Object workingtag = gettoCompount(rootnbttag, comp);
ReflectionMethod.COMPOUND_REMOVE_KEY.run(workingtag, key);
comp.setCompound(rootnbttag);
}
/**
* Gets the Keyset inside this Compound
*
* @param comp
* @return Set of all keys
*/
@SuppressWarnings("unchecked")
public static Set<String> getKeys(NBTCompound comp) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
return (Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(workingtag);
}
/**
* Sets data inside the Compound
*
* @param comp
* @param type
* @param key
* @param data
*/
public static void setData(NBTCompound comp, ReflectionMethod type, String key, Object data) {
if (data == null) {
remove(comp, key);
return;
}
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
type.run(workingtag, key, data);
comp.setCompound(rootnbttag);
}
/**
* Gets data from the Compound
*
* @param comp
* @param type
* @param key
* @return The value or default fallback from NMS
*/
public static Object getData(NBTCompound comp, ReflectionMethod type, String key) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
return null;
}
if (!valideCompound(comp))
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
Object workingtag = gettoCompount(rootnbttag, comp);
return type.run(workingtag, key);
}
}

View File

@ -0,0 +1,42 @@
package com.pretzel.dev.villagertradelimiter.nms;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
/**
* String implementation for NBTLists
*
* @author tr7zw
*
*/
public class NBTStringList extends NBTList<String> {
protected NBTStringList(NBTCompound owner, String name, NBTType type, Object list) {
super(owner, name, type, list);
}
@Override
public String get(int index) {
try {
return (String) ReflectionMethod.LIST_GET_STRING.run(listObject, index);
} catch (Exception ex) {
throw new NbtApiException(ex);
}
}
@Override
protected Object asTag(String object) {
try {
Constructor<?> con = ClassWrapper.NMS_NBTTAGSTRING.getClazz().getDeclaredConstructor(String.class);
con.setAccessible(true);
return con.newInstance(object);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
}
}
}

View File

@ -1,71 +0,0 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util;
public class NBTTagCompound {
private final NMS nms;
private final Class<?> c;
private final Object self;
public NBTTagCompound(final NMS nms, final Object self) {
this.nms = nms;
this.self = self;
this.c = self.getClass();
}
public Object get(final String key) {
try {
return this.nms.getMethod(this.c, "get", String.class).invoke(this.self, key);
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public String getString(final String key) {
try {
return (String)this.nms.getMethod(this.c, "getString", String.class).invoke(this.self, key);
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public int getInt(final String key) {
try {
return (int)this.nms.getMethod(this.c, "getInt", String.class).invoke(this.self, key);
} catch (Exception e) {
Util.errorMsg(e);
return Integer.MIN_VALUE;
}
}
public int[] getIntArray(final String key) {
try {
return (int[])this.nms.getMethod(this.c, "getIntArray", String.class).invoke(this.self, key);
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public NBTTagCompound getCompound(final String key) {
try {
return new NBTTagCompound(this.nms, this.nms.getMethod(this.self.getClass(), "getCompound", String.class).invoke(this.self, key));
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public void setInt(final String key, final int value) {
try {
this.nms.getMethod(this.c, "setInt", String.class, Integer.TYPE).invoke(this.self, key, value);
} catch (Exception e) {
Util.errorMsg(e);
}
}
public Object getSelf() { return this.self; }
public Class<?> getC() { return this.c; }
}

View File

@ -1,41 +0,0 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util;
public class NBTTagList
{
private final NMS nms;
private final Object self;
private final Class<?> c;
public NBTTagList(final NMS nms, final Object self) {
this.nms = nms;
this.self = self;
if(nms.getVersion().compareTo("v1_17_R1") < 0)
this.c = nms.getNMSClass("server."+nms.getVersion()+".NBTTagList");
else
this.c = nms.getNMSClass("nbt.NBTTagList");
}
public NBTTagCompound getCompound(final int index) {
try {
return new NBTTagCompound(this.nms, this.nms.getMethod(this.c, "getCompound", Integer.TYPE).invoke(this.self, index));
} catch (Exception e) {
Util.errorMsg(e);
return null;
}
}
public int size() {
try {
return (int)this.nms.getMethod(this.c, "size").invoke(this.self, new Object[0]);
} catch (Exception e) {
Util.errorMsg(e);
return -1;
}
}
public Object getSelf() {
return this.self;
}
}

View File

@ -0,0 +1,64 @@
package com.pretzel.dev.villagertradelimiter.nms;
import org.bukkit.block.BlockState;
import de.tr7zw.annotations.FAUtil;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.AvailableSince;
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.CheckUtil;
/**
* NBT class to access vanilla tags from TileEntities. TileEntities don't
* support custom tags. Use the NBTInjector for custom tags. Changes will be
* instantly applied to the Tile, use the merge method to do many things at
* once.
*
* @author tr7zw
*
*/
public class NBTTileEntity extends NBTCompound {
private final BlockState tile;
/**
* @param tile BlockState from any TileEntity
*/
public NBTTileEntity(BlockState tile) {
super(null, null);
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
throw new NullPointerException("Tile can't be null/not placed!");
}
this.tile = tile;
}
@Override
public Object getCompound() {
return NBTReflectionUtil.getTileEntityNBTTagCompound(tile);
}
@Override
protected void setCompound(Object compound) {
NBTReflectionUtil.setTileEntityNBTTagCompound(tile, compound);
}
/**
* Gets the NBTCompound used by spigots PersistentDataAPI. This method is only
* available for 1.14+!
*
* @return NBTCompound containing the data of the PersistentDataAPI
*/
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
if (hasKey("PublicBukkitValues")) {
return getCompound("PublicBukkitValues");
} else {
NBTContainer container = new NBTContainer();
container.addCompound("PublicBukkitValues").setString("__nbtapi",
"Marker to make the PersistentDataContainer have content");
mergeCompound(container);
return getCompound("PublicBukkitValues");
}
}
}

View File

@ -0,0 +1,48 @@
package com.pretzel.dev.villagertradelimiter.nms;
/**
* Enum of all NBT Types Minecraft contains
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum NBTType {
NBTTagEnd(0),
NBTTagByte(1),
NBTTagShort(2),
NBTTagInt(3),
NBTTagLong(4),
NBTTagFloat(5),
NBTTagDouble(6),
NBTTagByteArray(7),
NBTTagIntArray(11),
NBTTagString(8),
NBTTagList(9),
NBTTagCompound(10);
NBTType(int i) {
id = i;
}
private final int id;
/**
* @return Id used by Minecraft internally
*/
public int getId() {
return id;
}
/**
* @param id Internal Minecraft id
* @return Enum representing the id, NBTTagEnd for invalide ids
*/
public static NBTType valueOf(int id) {
for (NBTType t : values())
if (t.getId() == id)
return t;
return NBTType.NBTTagEnd;
}
}

View File

@ -1,82 +0,0 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util;
import java.lang.reflect.Method;
import java.util.HashMap;
public class NMS
{
private final HashMap<String, Class<?>> nmsClasses;
private final HashMap<String, Class<?>> bukkitClasses;
private final HashMap<String, Method> methods;
private final String version;
public NMS(final String version) {
this.nmsClasses = new HashMap<>();
this.bukkitClasses = new HashMap<>();
this.methods = new HashMap<>();
this.version = version;
}
public Class<?> getNMSClass(final String name) {
if(this.nmsClasses.containsKey(name)) {
return this.nmsClasses.get(name);
}
try {
Class<?> c = Class.forName("net.minecraft."+name);
this.nmsClasses.put(name, c);
return c;
} catch (Exception e) {
Util.errorMsg(e);
return this.nmsClasses.put(name, null);
}
}
public Class<?> getCraftBukkitClass(final String name) {
if(this.bukkitClasses.containsKey(name)) {
return this.bukkitClasses.get(name);
}
try {
Class<?> c = Class.forName("org.bukkit.craftbukkit." + this.version + "." + name);
this.bukkitClasses.put(name, c);
return c;
} catch (Exception e) {
Util.errorMsg(e);
return this.bukkitClasses.put(name, null);
}
}
public Method getMethod(final Class<?> invoker, final String name) throws NoSuchMethodException {
return this.getMethod(invoker, name, null, null);
}
public Method getMethod(final Class<?> invoker, final String name, final Class<?> type) throws NoSuchMethodException {
return this.getMethod(invoker, name, type, null);
}
public Method getMethod(final Class<?> invoker, final String name, final Class<?> type, final Class<?> type2) throws NoSuchMethodException {
if(this.methods.containsKey(name) && this.methods.get(name).getDeclaringClass().equals(invoker)) {
return this.methods.get(name);
}
Method method;
try {
if(type2 != null) {
method = invoker.getMethod(name, type, type2);
} else if(type != null) {
method = invoker.getMethod(name, type);
} else {
method = invoker.getMethod(name);
}
} catch (Exception e) {
Util.errorMsg(e);
return this.methods.put(name, null);
}
this.methods.put(name, method);
return method;
}
public String getVersion() { return this.version; }
}

View File

@ -1,38 +0,0 @@
package com.pretzel.dev.villagertradelimiter.nms;
import com.pretzel.dev.villagertradelimiter.lib.Util;
public class NMSEntity {
private final NMS nms;
private final Class<?> c;
public NMSEntity(final NMS nms) {
this.nms = nms;
if(nms.getVersion().compareTo("v1_17_R1") < 0)
this.c = nms.getNMSClass("server."+nms.getVersion()+".Entity");
else
this.c = nms.getNMSClass("world.entity.Entity");
}
public void save(final Object nmsEntity, final NBTTagCompound tag) {
try {
nms.getMethod(this.c, "save", tag.getC()).invoke(nmsEntity, tag.getSelf());
} catch (Exception e) {
Util.errorMsg(e);
}
}
public void load(final Object nmsEntity, final NBTTagCompound tag) {
try {
nms.getMethod(this.c, "load", tag.getC()).invoke(nmsEntity, tag.getSelf());
} catch (NoSuchMethodException e) {
try {
nms.getMethod(this.c, "f", tag.getC()).invoke(nmsEntity, tag.getSelf());
} catch (Exception e2) {
Util.errorMsg(e2);
}
} catch (Exception e3) {
Util.errorMsg(e3);
}
}
}

View File

@ -0,0 +1,56 @@
package com.pretzel.dev.villagertradelimiter.nms;
/**
* A generic {@link RuntimeException} that can be thrown by most methods in the
* NBTAPI.
*
* @author tr7zw
*
*/
public class NbtApiException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -993309714559452334L;
/**
*
*/
public NbtApiException() {
super();
}
/**
* @param message
* @param cause
* @param enableSuppression
* @param writableStackTrace
*/
public NbtApiException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
/**
* @param message
* @param cause
*/
public NbtApiException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
*/
public NbtApiException(String message) {
super(message);
}
/**
* @param cause
*/
public NbtApiException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,388 @@
package com.pretzel.dev.villagertradelimiter.nms.utils;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*
* This class is modified by tr7zw to work when the api is shaded into other peoples plugins.
*/
public class ApiMetricsLite {
private static final String PLUGINNAME = "ItemNBTAPI"; // DO NOT CHANGE THE NAME! else it won't link the data on bStats
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The version of the NBT-Api bStats
public static final int NBT_BSTATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private Plugin plugin;
/**
* Class constructor.
*
*/
public ApiMetricsLite() {
// The register method just uses any enabled plugin it can find to register. This *shouldn't* cause any problems, since the plugin isn't used any other way.
// Register our service
for(Plugin plug : Bukkit.getPluginManager().getPlugins()) {
plugin = plug;
if(plugin != null)
break;
}
if(plugin == null) {
return;// Didn't find any plugin that could work
}
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
"To honor their work, you should not disable it.\n" +
"This has nearly no effect on the server performance!\n" +
"Check out https://bStats.org/ to learn more :)"
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) { }
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
enabled = config.getBoolean("enabled", true);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("NBT_BSTATS_VERSION"); // Create only one instance of the nbt-api bstats.
return;
} catch (NoSuchFieldException ignored) { }
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) { }
}
boolean fFound = found;
// Register our service
if(Bukkit.isPrimaryThread()){
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
if (!fFound) {
getLogger().info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
// We are the first!
startSubmitting();
}
}else{
Bukkit.getScheduler().runTask(plugin, () -> {
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
if (!fFound) {
getLogger().info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
// We are the first!
startSubmitting();
}
});
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000l * 60l * 5l, 1000l * 60l * 30l);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
data.addProperty("pluginName", PLUGINNAME); // Append the name of the plugin
data.addProperty("pluginVersion", MinecraftVersion.VERSION); // Append the version of the plugin
data.add("customCharts", new JsonArray());
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
getLogger().log(Level.WARNING, "[NBTAPI][BSTATS] Encountered exception while posting request!", e);
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
//this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
}
continue; // continue looping since we cannot do any other thing.
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
}
}
} catch (NoSuchFieldException ignored) { }
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
getLogger().log(Level.WARNING, "[NBTAPI][BSTATS] Could not submit plugin stats of " + plugin.getName(), e);
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
//plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
System.out.println("[NBTAPI][BSTATS] Sending data to bStats: " + data.toString());
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
//plugin.getLogger().info("Sending data to bStats: " + data.toString());
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
bufferedReader.close();
if (logResponseStatusText) {
getLogger().info("[NBTAPI][BSTATS] Sent data to bStats and received response: " + builder.toString());
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
//plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return new byte[0];
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
}

View File

@ -0,0 +1,54 @@
package com.pretzel.dev.villagertradelimiter.nms.utils;
import com.google.gson.Gson;
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
/**
* Helper class for 1.7 servers without Gson
*
* @author tr7zw
*
*/
public class GsonWrapper {
/**
* Private constructor
*/
private GsonWrapper() {
}
private static final Gson gson = new Gson();
/**
* Turns Objects into Json Strings
*
* @param obj
* @return Json, representing the Object
*/
public static String getString(Object obj) {
return gson.toJson(obj);
}
/**
* Creates an Object of the given type using the Json String
*
* @param json
* @param type
* @return Object that got created, or null if the json is null
*/
public static <T> T deserializeJson(String json, Class<T> type) {
try {
if (json == null) {
return null;
}
T obj = gson.fromJson(json, type);
return type.cast(obj);
} catch (Exception ex) {
throw new NbtApiException("Error while converting json to " + type.getName(), ex);
}
}
}

View File

@ -0,0 +1,210 @@
package com.pretzel.dev.villagertradelimiter.nms.utils;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
/**
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
* other classes,registers bStats and checks rather Maven shading was done
* correctly.
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum MinecraftVersion {
UNKNOWN(Integer.MAX_VALUE), // Use the newest known mappings
MC1_7_R4(174), MC1_8_R3(183), MC1_9_R1(191), MC1_9_R2(192), MC1_10_R1(1101), MC1_11_R1(1111), MC1_12_R1(1121),
MC1_13_R1(1131), MC1_13_R2(1132), MC1_14_R1(1141), MC1_15_R1(1151), MC1_16_R1(1161), MC1_16_R2(1162), MC1_16_R3(1163), MC1_17_R1(1171), MC1_18_R1(1181, true);
private static MinecraftVersion version;
private static Boolean hasGsonSupport;
private static boolean bStatsDisabled = false;
private static boolean disablePackageWarning = false;
private static boolean updateCheckDisabled = false;
/**
* Logger used by the api
*/
private static Logger logger = Logger.getLogger("NBTAPI");
// NBT-API Version
protected static final String VERSION = "2.9.0-SNAPSHOT";
private final int versionId;
private final boolean mojangMapping;
MinecraftVersion(int versionId) {
this(versionId, false);
}
MinecraftVersion(int versionId, boolean mojangMapping) {
this.versionId = versionId;
this.mojangMapping = mojangMapping;
}
/**
* @return A simple comparable Integer, representing the version.
*/
public int getVersionId() {
return versionId;
}
/**
* @return True if method names are in Mojang format and need to be remapped internally
*/
public boolean isMojangMapping() {
return mojangMapping;
}
public String getPackageName() {
if(this == UNKNOWN) {
return values()[values().length-1].name().replace("MC", "v");
}
return this.name().replace("MC", "v");
}
/**
* Returns true if the current versions is at least the given Version
*
* @param version The minimum version
* @return
*/
public static boolean isAtLeastVersion(MinecraftVersion version) {
return getVersion().getVersionId() >= version.getVersionId();
}
/**
* Returns true if the current versions newer (not equal) than the given version
*
* @param version The minimum version
* @return
*/
public static boolean isNewerThan(MinecraftVersion version) {
return getVersion().getVersionId() > version.getVersionId();
}
/**
* Getter for this servers MinecraftVersion. Also init's bStats and checks the
* shading.
*
* @return The enum for the MinecraftVersion this server is running
*/
public static MinecraftVersion getVersion() {
if (version != null) {
return version;
}
final String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
logger.info("[NBTAPI] Found Spigot: " + ver + "! Trying to find NMS support");
try {
version = MinecraftVersion.valueOf(ver.replace("v", "MC"));
} catch (IllegalArgumentException ex) {
version = MinecraftVersion.UNKNOWN;
}
if (version != UNKNOWN) {
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
} else {
logger.warning("[NBTAPI] Wasn't able to find NMS Support! Some functions may not work!");
}
init();
return version;
}
private static void init() {
try {
if (hasGsonSupport() && !bStatsDisabled)
new ApiMetricsLite();
} catch (Exception ex) {
logger.log(Level.WARNING, "[NBTAPI] Error enabling Metrics!", ex);
}
if (hasGsonSupport() && !updateCheckDisabled)
new Thread(() -> {
try {
VersionChecker.checkForUpdates();
} catch (Exception ex) {
logger.log(Level.WARNING, "[NBTAPI] Error while checking for updates!", ex);
}
}).start();
// Maven's Relocate is clever and changes strings, too. So we have to use this
// little "trick" ... :D (from bStats)
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
if (!disablePackageWarning && MinecraftVersion.class.getPackage().getName().equals(defaultPackage)) {
logger.warning(
"#########################################- NBTAPI -#########################################");
logger.warning(
"The NBT-API package has not been moved! This *will* cause problems with other plugins containing");
logger.warning(
"a different version of the api! Please read the guide on the plugin page on how to get the");
logger.warning(
"Maven Shade plugin to relocate the api to your personal location! If you are not the developer,");
logger.warning("please check your plugins and contact their developer, so he can fix this issue.");
logger.warning(
"#########################################- NBTAPI -#########################################");
}
}
/**
* @return True, if Gson is usable
*/
public static boolean hasGsonSupport() {
if (hasGsonSupport != null) {
return hasGsonSupport;
}
try {
logger.info("[NBTAPI] Found Gson: " + Class.forName("com.google.gson.Gson"));
hasGsonSupport = true;
} catch (Exception ex) {
logger.info("[NBTAPI] Gson not found! This will not allow the usage of some methods!");
hasGsonSupport = false;
}
return hasGsonSupport;
}
/**
* Calling this function before the NBT-Api is used will disable bStats stats
* collection. Please consider not to do that, since it won't affect your plugin
* and helps the NBT-Api developer to see api's demand.
*/
public static void disableBStats() {
bStatsDisabled = true;
}
/**
* Disables the update check. Uses Spiget to get the current version and prints
* a warning when outdated.
*/
public static void disableUpdateCheck() {
updateCheckDisabled = true;
}
/**
* Forcefully disables the log message for plugins not shading the API to
* another location. This may be helpful for networks or development
* environments, but please don't use it for plugins that are uploaded to
* Spigotmc.
*/
public static void disablePackageWarning() {
disablePackageWarning = true;
}
/**
* @return Logger used by the NBT-API
*/
public static Logger getLogger() {
return logger;
}
/**
* Replaces the NBT-API logger with a custom implementation.
*
* @param logger The new logger(can not be null!)
*/
public static void replaceLogger(Logger logger) {
if(logger == null)throw new NullPointerException("Logger can not be null!");
MinecraftVersion.logger = logger;
}
}

View File

@ -0,0 +1,53 @@
package com.pretzel.dev.villagertradelimiter.nms.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
public final class ReflectionUtil {
private static Field field_modifiers;
static {
try {
field_modifiers = Field.class.getDeclaredField("modifiers");
field_modifiers.setAccessible(true);
} catch (NoSuchFieldException ex) {
try {
// This hacky workaround is for newer jdk versions 11+?
Method fieldGetter = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
fieldGetter.setAccessible(true);
Field[] fields = (Field[]) fieldGetter.invoke(Field.class, false);
for (Field f : fields)
if (f.getName().equals("modifiers")) {
field_modifiers = f;
field_modifiers.setAccessible(true);
break;
}
} catch (Exception e) {
throw new NbtApiException(e);
}
}
if (field_modifiers == null) {
throw new NbtApiException("Unable to init the modifiers Field.");
}
}
public static Field makeNonFinal(Field field) throws IllegalArgumentException, IllegalAccessException {
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
field_modifiers.set(field, mods & ~Modifier.FINAL);
}
return field;
}
public static void setFinal(Object obj, Field field, Object newValue)
throws IllegalArgumentException, IllegalAccessException {
field.setAccessible(true);
field = makeNonFinal(field);
field.set(obj, newValue);
}
}

View File

@ -0,0 +1,106 @@
package com.pretzel.dev.villagertradelimiter.nms.utils;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.pretzel.dev.villagertradelimiter.nms.NBTItem;
/**
* This class uses the Spiget API to check for updates
*
*/
public class VersionChecker {
private static final String USER_AGENT = "nbt-api Version check";
private static final String REQUEST_URL = "https://api.spiget.org/v2/resources/7939/versions?size=100";
protected static void checkForUpdates() throws Exception {
URL url = new URL(REQUEST_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.addRequestProperty("User-Agent", USER_AGENT);// Set
// User-Agent
// If you're not sure if the request will be successful,
// you need to check the response code and use #getErrorStream if it
// returned an error code
InputStream inputStream = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream);
// This could be either a JsonArray or JsonObject
JsonElement element = new JsonParser().parse(reader);
if (element.isJsonArray()) {
// Is JsonArray
JsonArray updates = (JsonArray) element;
JsonObject latest = (JsonObject) updates.get(updates.size() - 1);
int versionDifference = getVersionDifference(latest.get("name").getAsString());
if (versionDifference == -1) { // Outdated
MinecraftVersion.getLogger().log(Level.WARNING,
"[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage() + "' seems to be outdated!");
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
MinecraftVersion.getLogger().log(Level.WARNING,
"[NBTAPI] Please update the nbt-api or the plugin that contains the api!");
} else if (versionDifference == 0) {
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] The NBT-API seems to be up-to-date!");
} else if (versionDifference == 1) {
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage()
+ "' seems to be a future Version, not yet released on Spigot/CurseForge!");
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
}
} else {
// wut?!
MinecraftVersion.getLogger().log(Level.WARNING,
"[NBTAPI] Error when looking for Updates! Got non Json Array: '" + element.toString() + "'");
}
}
// -1 = we are outdated
// 0 = up to date
// 1 = using a future version
// This method is only able to compare the Format 0.0.0(-SNAPSHOT)
private static int getVersionDifference(String version) {
String current = MinecraftVersion.VERSION;
if (current.equals(version))
return 0;
String pattern = "\\.";
if (current.split(pattern).length != 3 || version.split(pattern).length != 3)
return -1;
int curMaj = Integer.parseInt(current.split(pattern)[0]);
int curMin = Integer.parseInt(current.split(pattern)[1]);
String curPatch = current.split(pattern)[2];
int relMaj = Integer.parseInt(version.split(pattern)[0]);
int relMin = Integer.parseInt(version.split(pattern)[1]);
String relPatch = version.split(pattern)[2];
if (curMaj < relMaj)
return -1;
if (curMaj > relMaj)
return 1;
if (curMin < relMin)
return -1;
if (curMin > relMin)
return 1;
int curPatchN = Integer.parseInt(curPatch.split("-")[0]);
int relPatchN = Integer.parseInt(relPatch.split("-")[0]);
if (curPatchN < relPatchN)
return -1;
if (curPatchN > relPatchN)
return 1;
if (!relPatch.contains("-") && curPatch.contains("-"))
return -1; // Release has no - but we do = We use a Snapshot of the
// release
if (relPatch.contains("-") && curPatch.contains("-"))
return 0; // Release and cur are Snapshots/alpha/beta
return 1;
}
}

View File

@ -0,0 +1,17 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.annotations;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
@Retention(RUNTIME)
@Target({ METHOD })
public @interface AvailableSince {
MinecraftVersion version();
}

View File

@ -0,0 +1,16 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.annotations;
import java.lang.reflect.Method;
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
public class CheckUtil {
public static boolean isAvaliable(Method method) {
if(MinecraftVersion.getVersion().getVersionId() < method.getAnnotation(AvailableSince.class).version().getVersionId())
throw new NbtApiException("The Method '" + method.getName() + "' is only avaliable for the Versions " + method.getAnnotation(AvailableSince.class).version() + "+, but still got called!");
return true;
}
}

View File

@ -0,0 +1,103 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
import java.util.logging.Level;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
/**
* Wraps NMS and CRAFT classes
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum ClassWrapper {
CRAFT_ITEMSTACK(PackageWrapper.CRAFTBUKKIT, "inventory.CraftItemStack", null, null),
CRAFT_METAITEM(PackageWrapper.CRAFTBUKKIT, "inventory.CraftMetaItem", null, null),
CRAFT_ENTITY(PackageWrapper.CRAFTBUKKIT, "entity.CraftEntity", null, null),
CRAFT_WORLD(PackageWrapper.CRAFTBUKKIT, "CraftWorld", null, null),
CRAFT_PERSISTENTDATACONTAINER(PackageWrapper.CRAFTBUKKIT, "persistence.CraftPersistentDataContainer",
MinecraftVersion.MC1_14_R1, null),
NMS_NBTBASE(PackageWrapper.NMS, "NBTBase", null, null, "net.minecraft.nbt", "net.minecraft.nbt.Tag"),
NMS_NBTTAGSTRING(PackageWrapper.NMS, "NBTTagString", null, null, "net.minecraft.nbt", "net.minecraft.nbt.StringTag"),
NMS_NBTTAGINT(PackageWrapper.NMS, "NBTTagInt", null, null, "net.minecraft.nbt", "net.minecraft.nbt.IntTag"),
NMS_NBTTAGFLOAT(PackageWrapper.NMS, "NBTTagFloat", null, null, "net.minecraft.nbt", "net.minecraft.nbt.FloatTag"),
NMS_NBTTAGDOUBLE(PackageWrapper.NMS, "NBTTagDouble", null, null, "net.minecraft.nbt", "net.minecraft.nbt.DoubleTag"),
NMS_NBTTAGLONG(PackageWrapper.NMS, "NBTTagLong", null, null, "net.minecraft.nbt", "net.minecraft.nbt.LongTag"),
NMS_ITEMSTACK(PackageWrapper.NMS, "ItemStack", null, null, "net.minecraft.world.item", "net.minecraft.world.item.ItemStack"),
NMS_NBTTAGCOMPOUND(PackageWrapper.NMS, "NBTTagCompound", null, null, "net.minecraft.nbt", "net.minecraft.nbt.CompoundTag"),
NMS_NBTTAGLIST(PackageWrapper.NMS, "NBTTagList", null, null, "net.minecraft.nbt", "net.minecraft.nbt.ListTag"),
NMS_NBTCOMPRESSEDSTREAMTOOLS(PackageWrapper.NMS, "NBTCompressedStreamTools", null, null, "net.minecraft.nbt", "net.minecraft.nbt.NbtIo"),
NMS_MOJANGSONPARSER(PackageWrapper.NMS, "MojangsonParser", null, null, "net.minecraft.nbt", "net.minecraft.nbt.TagParser"),
NMS_TILEENTITY(PackageWrapper.NMS, "TileEntity", null, null, "net.minecraft.world.level.block.entity", "net.minecraft.world.level.block.entity.BlockEntity"),
NMS_BLOCKPOSITION(PackageWrapper.NMS, "BlockPosition", MinecraftVersion.MC1_8_R3, null, "net.minecraft.core", "net.minecraft.core.BlockPos"),
NMS_WORLDSERVER(PackageWrapper.NMS, "WorldServer", null, null, "net.minecraft.server.level", "net.minecraft.server.level.ServerLevel"),
NMS_MINECRAFTSERVER(PackageWrapper.NMS, "MinecraftServer", null, null, "net.minecraft.server", "net.minecraft.server.MinecraftServer"),
NMS_WORLD(PackageWrapper.NMS, "World", null, null, "net.minecraft.world.level", "net.minecraft.world.level.Level"),
NMS_ENTITY(PackageWrapper.NMS, "Entity", null, null, "net.minecraft.world.entity", "net.minecraft.world.entity.Entity"),
NMS_ENTITYTYPES(PackageWrapper.NMS, "EntityTypes", null, null, "net.minecraft.world.entity", "net.minecraft.world.entity.EntityType"),
NMS_REGISTRYSIMPLE(PackageWrapper.NMS, "RegistrySimple", MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_12_R1),
NMS_REGISTRYMATERIALS(PackageWrapper.NMS, "RegistryMaterials", null, null, "net.minecraft.core", "net.minecraft.core.MappedRegistry"),
NMS_IREGISTRY(PackageWrapper.NMS, "IRegistry", null, null, "net.minecraft.core", "net.minecraft.core.Registry"),
NMS_MINECRAFTKEY(PackageWrapper.NMS, "MinecraftKey", MinecraftVersion.MC1_8_R3, null, "net.minecraft.resources", "net.minecraft.resources.ResourceKey"),
NMS_GAMEPROFILESERIALIZER(PackageWrapper.NMS, "GameProfileSerializer", null, null, "net.minecraft.nbt", "net.minecraft.nbt.NbtUtils"),
NMS_IBLOCKDATA(PackageWrapper.NMS, "IBlockData", MinecraftVersion.MC1_8_R3, null,
"net.minecraft.world.level.block.state", "net.minecraft.world.level.block.state.BlockState"),
GAMEPROFILE(PackageWrapper.NONE, "com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3, null);
private Class<?> clazz;
private boolean enabled = false;
private final String mojangName;
ClassWrapper(PackageWrapper packageId, String clazzName, MinecraftVersion from, MinecraftVersion to) {
this(packageId, clazzName, from, to, null, null);
}
ClassWrapper(PackageWrapper packageId, String clazzName, MinecraftVersion from, MinecraftVersion to,
String mojangMap, String mojangName) {
this.mojangName = mojangName;
if (from != null && MinecraftVersion.getVersion().getVersionId() < from.getVersionId()) {
return;
}
if (to != null && MinecraftVersion.getVersion().getVersionId() > to.getVersionId()) {
return;
}
enabled = true;
try {
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1) && mojangMap != null) {
clazz = Class.forName(mojangMap + "." + clazzName);
} else if (packageId == PackageWrapper.NONE) {
clazz = Class.forName(clazzName);
} else {
String version = MinecraftVersion.getVersion().getPackageName();
clazz = Class.forName(packageId.getUri() + "." + version + "." + clazzName);
}
} catch (Throwable ex) {
getLogger().log(Level.WARNING, "[NBTAPI] Error while trying to resolve the class '" + clazzName + "'!", ex);
}
}
/**
* @return The wrapped class
*/
public Class<?> getClazz() {
return clazz;
}
/**
* @return Is this class available in this Version
*/
public boolean isEnabled() {
return enabled;
}
/**
* @return Package+Class name used by Mojang
*/
public String getMojangName() {
return mojangName;
}
}

View File

@ -0,0 +1,80 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
import java.util.HashMap;
import java.util.Map;
/**
* Temporary solution to hold Mojang to unmapped Spigot mappings.
*
* @author tr7zw
*
*/
public class MojangToMapping {
@SuppressWarnings("serial")
private static Map<String, String> MC1_18R1 = new HashMap<String, String>() {
{
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "e");
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "p");
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String,int)", "c");
put("net.minecraft.nbt.CompoundTag#putByteArray(java.lang.String,byte[])", "a");
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "k");
put("net.minecraft.nbt.CompoundTag#putDouble(java.lang.String,double)", "a");
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "m");
put("net.minecraft.nbt.CompoundTag#putInt(java.lang.String,int)", "a");
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "n");
put("net.minecraft.nbt.CompoundTag#remove(java.lang.String)", "r");
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "c");
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
put("net.minecraft.nbt.CompoundTag#putBoolean(java.lang.String,boolean)", "a");
put("net.minecraft.nbt.CompoundTag#getTagType(java.lang.String)", "d");
put("net.minecraft.nbt.CompoundTag#putLong(java.lang.String,long)", "a");
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "l");
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "h");
put("net.minecraft.nbt.CompoundTag#putString(java.lang.String,java.lang.String)", "a");
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "f");
put("net.minecraft.nbt.CompoundTag#putIntArray(java.lang.String,int[])", "a");
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "g");
put("net.minecraft.nbt.CompoundTag#putByte(java.lang.String,byte)", "a");
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
put("net.minecraft.nbt.CompoundTag#putUUID(java.lang.String,java.util.UUID)", "a");
put("net.minecraft.nbt.CompoundTag#putShort(java.lang.String,short)", "a");
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "i");
put("net.minecraft.nbt.CompoundTag#putFloat(java.lang.String,float)", "a");
put("net.minecraft.nbt.CompoundTag#getBoolean(java.lang.String)", "q");
put("net.minecraft.nbt.CompoundTag#getUUID(java.lang.String)", "a");
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "j");
put("net.minecraft.nbt.ListTag#addTag(int,net.minecraft.nbt.Tag)", "b");
put("net.minecraft.nbt.ListTag#setTag(int,net.minecraft.nbt.Tag)", "a");
put("net.minecraft.nbt.ListTag#getString(int)", "j");
put("net.minecraft.nbt.ListTag#remove(int)", "remove");
put("net.minecraft.nbt.ListTag#getCompound(int)", "a");
put("net.minecraft.nbt.ListTag#size()", "size");
put("net.minecraft.nbt.ListTag#get(int)", "get");
put("net.minecraft.nbt.NbtIo#readCompressed(java.io.InputStream)", "a");
put("net.minecraft.nbt.NbtIo#writeCompressed(net.minecraft.nbt.CompoundTag,java.io.OutputStream)", "a");
put("net.minecraft.nbt.NbtUtils#readGameProfile(net.minecraft.nbt.CompoundTag)", "a");
put("net.minecraft.nbt.NbtUtils#writeGameProfile(net.minecraft.nbt.CompoundTag,com.mojang.authlib.GameProfile)", "a");
put("net.minecraft.nbt.TagParser#parseTag(java.lang.String)", "a");
put("net.minecraft.world.entity.Entity#getEncodeId()", "bk");
put("net.minecraft.world.entity.Entity#load(net.minecraft.nbt.CompoundTag)", "g");
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.nbt.CompoundTag)", "f");
put("net.minecraft.world.item.ItemStack#setTag(net.minecraft.nbt.CompoundTag)", "c");
put("net.minecraft.world.item.ItemStack#getTag()", "s");
put("net.minecraft.world.item.ItemStack#save(net.minecraft.nbt.CompoundTag)", "b");
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId()", "n");
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "q");
put("net.minecraft.world.level.block.entity.BlockEntity#load(net.minecraft.nbt.CompoundTag)", "a");
put("net.minecraft.server.level.ServerLevel#getBlockEntity(net.minecraft.core.BlockPos)", "c_");
}
};
public static Map<String, String> getMapping(){
return MC1_18R1;
}
}

View File

@ -0,0 +1,56 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
import java.lang.reflect.Constructor;
import java.util.logging.Level;
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
/**
* This Enum wraps Constructors for NMS classes
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum ObjectCreator {
NMS_NBTTAGCOMPOUND(null, null, ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),
NMS_BLOCKPOSITION(null, null, ClassWrapper.NMS_BLOCKPOSITION.getClazz(), int.class, int.class, int.class),
NMS_COMPOUNDFROMITEM(MinecraftVersion.MC1_11_R1, null, ClassWrapper.NMS_ITEMSTACK.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),;
private Constructor<?> construct;
private Class<?> targetClass;
ObjectCreator(MinecraftVersion from, MinecraftVersion to, Class<?> clazz, Class<?>... args) {
if (clazz == null)
return;
if (from != null && MinecraftVersion.getVersion().getVersionId() < from.getVersionId())
return;
if (to != null && MinecraftVersion.getVersion().getVersionId() > to.getVersionId())
return;
try {
this.targetClass = clazz;
construct = clazz.getDeclaredConstructor(args);
construct.setAccessible(true);
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Unable to find the constructor for the class '" + clazz.getName() + "'", ex);
}
}
/**
* Creates an Object instance with given args
*
* @param args
* @return Object created
*/
public Object getInstance(Object... args) {
try {
return construct.newInstance(args);
} catch (Exception ex) {
throw new NbtApiException("Exception while creating a new instance of '" + targetClass + "'", ex);
}
}
}

View File

@ -0,0 +1,29 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
/**
* Package enum
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum PackageWrapper {
NMS(new String(new byte[] {'n', 'e', 't', '.', 'm', 'i', 'n', 'e', 'c', 'r', 'a', 'f', 't', '.', 's', 'e', 'r', 'v', 'e', 'r'})),
CRAFTBUKKIT(new String(new byte[] {'o', 'r', 'g', '.', 'b', 'u', 'k', 'k', 'i', 't', '.', 'c', 'r', 'a', 'f', 't', 'b', 'u', 'k', 'k', 'i', 't'})),
NONE("")
;
private final String uri;
private PackageWrapper(String uri) {
this.uri = uri;
}
/**
* @return The Uri for that package
*/
public String getUri() {
return uri;
}
}

View File

@ -0,0 +1,225 @@
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.UUID;
import org.bukkit.inventory.ItemStack;
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
/**
* This class caches method reflections, keeps track of method name changes between versions and allows early checking for problems
*
* @author tr7zw
*
*/
@SuppressWarnings("javadoc")
public enum ReflectionMethod {
COMPOUND_SET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, float.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setFloat"), new Since(MinecraftVersion.MC1_18_R1, "putFloat(java.lang.String,float)")),
COMPOUND_SET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setString"), new Since(MinecraftVersion.MC1_18_R1, "putString(java.lang.String,java.lang.String)")),
COMPOUND_SET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setInt"), new Since(MinecraftVersion.MC1_18_R1, "putInt(java.lang.String,int)")),
COMPOUND_SET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, byte[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByteArray"), new Since(MinecraftVersion.MC1_18_R1, "putByteArray(java.lang.String,byte[])")),
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray"), new Since(MinecraftVersion.MC1_18_R1, "putIntArray(java.lang.String,int[])")),
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, long.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong"), new Since(MinecraftVersion.MC1_18_R1, "putLong(java.lang.String,long)")),
COMPOUND_SET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, short.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setShort"), new Since(MinecraftVersion.MC1_18_R1, "putShort(java.lang.String,short)")),
COMPOUND_SET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, byte.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByte"), new Since(MinecraftVersion.MC1_18_R1, "putByte(java.lang.String,byte)")),
COMPOUND_SET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, double.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setDouble"), new Since(MinecraftVersion.MC1_18_R1, "putDouble(java.lang.String,double)")),
COMPOUND_SET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, boolean.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean"), new Since(MinecraftVersion.MC1_18_R1, "putBoolean(java.lang.String,boolean)")),
COMPOUND_SET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, UUID.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"), new Since(MinecraftVersion.MC1_18_R1, "putUUID(java.lang.String,java.util.UUID)")),
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set"), new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_18_R1, "get(java.lang.String)")),
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList"), new Since(MinecraftVersion.MC1_18_R1, "getList(java.lang.String,int)")),
COMPOUND_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[]{}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTypeId")), // Only needed for 1.7.10 getType
COMPOUND_GET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getFloat"), new Since(MinecraftVersion.MC1_18_R1, "getFloat(java.lang.String)")),
COMPOUND_GET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString"), new Since(MinecraftVersion.MC1_18_R1, "getString(java.lang.String)")),
COMPOUND_GET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getInt"), new Since(MinecraftVersion.MC1_18_R1, "getInt(java.lang.String)")),
COMPOUND_GET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByteArray"), new Since(MinecraftVersion.MC1_18_R1, "getByteArray(java.lang.String)")),
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getIntArray"), new Since(MinecraftVersion.MC1_18_R1, "getIntArray(java.lang.String)")),
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getLong"), new Since(MinecraftVersion.MC1_18_R1, "getLong(java.lang.String)")),
COMPOUND_GET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getShort"), new Since(MinecraftVersion.MC1_18_R1, "getShort(java.lang.String)")),
COMPOUND_GET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByte"), new Since(MinecraftVersion.MC1_18_R1, "getByte(java.lang.String)")),
COMPOUND_GET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getDouble"), new Since(MinecraftVersion.MC1_18_R1, "getDouble(java.lang.String)")),
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getBoolean"), new Since(MinecraftVersion.MC1_18_R1, "getBoolean(java.lang.String)")),
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"), new Since(MinecraftVersion.MC1_18_R1, "getUUID(java.lang.String)")),
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getCompound"), new Since(MinecraftVersion.MC1_18_R1, "getCompound(java.lang.String)")),
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK, new Class[] {}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTag"), new Since(MinecraftVersion.MC1_18_R1, "getTag()")),
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "save"), new Since(MinecraftVersion.MC1_18_R1, "save(net.minecraft.nbt.CompoundTag)")),
NMSITEM_CREATESTACK(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_10_R1, new Since(MinecraftVersion.MC1_7_R4, "createStack")),
COMPOUND_REMOVE_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "remove"), new Since(MinecraftVersion.MC1_18_R1, "remove(java.lang.String)")),
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "hasKey"), new Since(MinecraftVersion.MC1_18_R1, "contains(java.lang.String)")),
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "b"), new Since(MinecraftVersion.MC1_9_R1, "d"), new Since(MinecraftVersion.MC1_15_R1, "e"), new Since(MinecraftVersion.MC1_16_R1, "d"), new Since(MinecraftVersion.MC1_18_R1, "getTagType(java.lang.String)")),
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"), new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"), new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")), // FIXME ?!?
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove"), new Since(MinecraftVersion.MC1_18_R1, "remove(int)")),
LIST_SIZE(ClassWrapper.NMS_NBTTAGLIST, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "size"), new Since(MinecraftVersion.MC1_18_R1, "size()")),
LIST_SET(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_13_R1, "set"), new Since(MinecraftVersion.MC1_18_R1, "setTag(int,net.minecraft.nbt.Tag)")),
LEGACY_LIST_ADD(ClassWrapper.NMS_NBTTAGLIST, new Class[]{ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_13_R2, new Since(MinecraftVersion.MC1_7_R4, "add")),
LIST_ADD(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "add"), new Since(MinecraftVersion.MC1_18_R1, "addTag(int,net.minecraft.nbt.Tag)")),
LIST_GET_STRING(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString"), new Since(MinecraftVersion.MC1_18_R1, "getString(int)")),
LIST_GET_COMPOUND(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_18_R1, "getCompound(int)")),
LIST_GET(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_8_R3, "g"), new Since(MinecraftVersion.MC1_9_R1, "h"), new Since(MinecraftVersion.MC1_12_R1, "i"), new Since(MinecraftVersion.MC1_13_R1, "get"), new Since(MinecraftVersion.MC1_18_R1, "get(int)")),
ITEMSTACK_SET_TAG(ClassWrapper.NMS_ITEMSTACK, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTag"), new Since(MinecraftVersion.MC1_18_R1, "setTag(net.minecraft.nbt.CompoundTag)")),
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK, new Class[]{ItemStack.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK, new Class[]{ClassWrapper.NMS_ITEMSTACK.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "getTileEntity"), new Since(MinecraftVersion.MC1_18_R1, "getBlockEntity(net.minecraft.core.BlockPos)")),
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_8_R3, "setTileEntity")),
NMS_WORLD_REMOVE_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_8_R3, "t"), new Since(MinecraftVersion.MC1_9_R1, "s"), new Since(MinecraftVersion.MC1_13_R1, "n"), new Since(MinecraftVersion.MC1_14_R1, "removeTileEntity")),
NMS_WORLD_GET_TILEENTITY_1_7_10(ClassWrapper.NMS_WORLDSERVER, new Class[]{int.class, int.class, int.class}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTileEntity")),
TILEENTITY_LOAD_LEGACY191(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_MINECRAFTSERVER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_9_R1, MinecraftVersion.MC1_9_R1, new Since(MinecraftVersion.MC1_9_R1, "a")), //FIXME: No Spigot mapping!
TILEENTITY_LOAD_LEGACY183(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_9_R2, new Since(MinecraftVersion.MC1_8_R3, "c"), new Since(MinecraftVersion.MC1_9_R1, "a"), new Since(MinecraftVersion.MC1_9_R2, "c")), //FIXME: No Spigot mapping!
TILEENTITY_LOAD_LEGACY1121(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_WORLD.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_10_R1, MinecraftVersion.MC1_12_R1, new Since(MinecraftVersion.MC1_10_R1, "a"), new Since(MinecraftVersion.MC1_12_R1, "create")),
TILEENTITY_LOAD_LEGACY1151(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_15_R1, new Since(MinecraftVersion.MC1_12_R1, "create")),
TILEENTITY_LOAD(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_16_R1, "create")),
TILEENTITY_GET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_7_R4, "b"), new Since(MinecraftVersion.MC1_9_R1, "save")),
TILEENTITY_GET_NBT_1181(ClassWrapper.NMS_TILEENTITY, new Class[]{}, MinecraftVersion.MC1_18_R1, new Since(MinecraftVersion.MC1_18_R1, "saveWithId()")),
TILEENTITY_SET_NBT_LEGACY1151(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_15_R1, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_12_R1, "load")),
TILEENTITY_SET_NBT_LEGACY1161(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_16_R1, "load")),
TILEENTITY_SET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_16_R1, "load"), new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
TILEENTITY_GET_BLOCKDATA(ClassWrapper.NMS_TILEENTITY, new Class[]{}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "getBlock"), new Since(MinecraftVersion.MC1_18_R1, "getBlockState()")),
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "f"), new Since(MinecraftVersion.MC1_16_R1, "load"), new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "e"), new Since(MinecraftVersion.MC1_12_R1, "save"), new Since(MinecraftVersion.MC1_18_R1, "saveWithoutId(net.minecraft.nbt.CompoundTag)")),
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY, new Class[]{}, MinecraftVersion.MC1_14_R1,new Since(MinecraftVersion.MC1_14_R1, "getSaveID"), new Since(MinecraftVersion.MC1_18_R1, "getEncodeId()")),
NBTFILE_READ(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS, new Class[]{InputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_18_R1, "readCompressed(java.io.InputStream)")),
NBTFILE_WRITE(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), OutputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_18_R1, "writeCompressed(net.minecraft.nbt.CompoundTag,java.io.OutputStream)")),
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "parse"), new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)")),
REGISTRY_KEYSET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
REGISTRY_GET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "get")),
REGISTRY_SET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{Object.class, Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "a")), //FIXME: No Spigot mapping!
REGISTRY_GET_INVERSE (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "b")), //FIXME: No Spigot mapping!
REGISTRYMATERIALS_KEYSET (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R1, "keySet")),
REGISTRYMATERIALS_GET (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{ClassWrapper.NMS_MINECRAFTKEY.getClazz()}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R1, "get")),
REGISTRYMATERIALS_GETKEY (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{Object.class}, MinecraftVersion.MC1_13_R2, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R2, "getKey")),
GAMEPROFILE_DESERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "deserialize"), new Since(MinecraftVersion.MC1_18_R1, "readGameProfile(net.minecraft.nbt.CompoundTag)")),
GAMEPROFILE_SERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.GAMEPROFILE.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "serialize"), new Since(MinecraftVersion.MC1_18_R1, "writeGameProfile(net.minecraft.nbt.CompoundTag,com.mojang.authlib.GameProfile)")),
CRAFT_PERSISTENT_DATA_CONTAINER_TO_TAG (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "toTagCompound")),
CRAFT_PERSISTENT_DATA_CONTAINER_GET_MAP (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "getRaw")),
CRAFT_PERSISTENT_DATA_CONTAINER_PUT_ALL (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "putAll")),
;
private MinecraftVersion removedAfter;
private Since targetVersion;
private Method method;
private boolean loaded = false;
private boolean compatible = false;
private String methodName = null;
private ClassWrapper parentClassWrapper;
ReflectionMethod(ClassWrapper targetClass, Class<?>[] args, MinecraftVersion addedSince, MinecraftVersion removedAfter, Since... methodnames){
this.removedAfter = removedAfter;
this.parentClassWrapper = targetClass;
if(!MinecraftVersion.isAtLeastVersion(addedSince) || (this.removedAfter != null && MinecraftVersion.isNewerThan(removedAfter)))return;
compatible = true;
MinecraftVersion server = MinecraftVersion.getVersion();
Since target = methodnames[0];
for(Since s : methodnames){
if(s.version.getVersionId() <= server.getVersionId() && target.version.getVersionId() < s.version.getVersionId())
target = s;
}
targetVersion = target;
String targetMethodName = targetVersion.name;
try{
if(targetVersion.version.isMojangMapping())
targetMethodName = MojangToMapping.getMapping().getOrDefault(targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
method = targetClass.getClazz().getDeclaredMethod(targetMethodName, args);
method.setAccessible(true);
loaded = true;
methodName = targetVersion.name;
}catch(NullPointerException | NoSuchMethodException | SecurityException ex){
try{
if(targetVersion.version.isMojangMapping())
targetMethodName = MojangToMapping.getMapping().getOrDefault(targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
method = targetClass.getClazz().getMethod(targetMethodName, args);
method.setAccessible(true);
loaded = true;
methodName = targetVersion.name;
}catch(NullPointerException | NoSuchMethodException | SecurityException ex2){
System.out.println("[NBTAPI] Unable to find the method '" + targetMethodName + "' in '" + (targetClass.getClazz() == null ? targetClass.getMojangName() : targetClass.getClazz().getSimpleName()) + "' Args: " + Arrays.toString(args) + " Enum: " + this); //NOSONAR This gets loaded before the logger is loaded
}
}
}
ReflectionMethod(ClassWrapper targetClass, Class<?>[] args, MinecraftVersion addedSince, Since... methodnames){
this(targetClass, args, addedSince, null, methodnames);
}
/**
* Runs the method on a given target object using the given args.
*
* @param target
* @param args
* @return Value returned by the method
*/
public Object run(Object target, Object... args){
if(method == null)
throw new NbtApiException("Method not loaded! '" + this + "'");
try{
return method.invoke(target, args);
}catch(Exception ex){
throw new NbtApiException("Error while calling the method '" + methodName + "', loaded: " + loaded + ", Enum: " + this + " Passed Class: " + target.getClass(), ex);
}
}
/**
* @return The MethodName, used in this Minecraft Version
*/
public String getMethodName() {
return methodName;
}
/**
* @return Has this method been linked
*/
public boolean isLoaded() {
return loaded;
}
/**
* @return Is this method available in this Minecraft Version
*/
public boolean isCompatible() {
return compatible;
}
public Since getSelectedVersionInfo() {
return targetVersion;
}
/**
* @return Get Wrapper of the parent class
*/
public ClassWrapper getParentClassWrapper() {
return parentClassWrapper;
}
public static class Since{
public final MinecraftVersion version;
public final String name;
public Since(MinecraftVersion version, String name) {
this.version = version;
this.name = name;
}
}
}