Updated NBT Api

This commit is contained in:
Brianna 2020-07-22 11:32:22 -05:00
parent b66120c10c
commit 99928f434b
21 changed files with 2147 additions and 1125 deletions

View File

@ -1,5 +1,8 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import java.io.InputStream;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ClassWrapper;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ObjectCreator;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ReflectionMethod;
@ -29,9 +32,25 @@ public class NBTContainer extends NBTCompound {
*/
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.
@ -40,6 +59,9 @@ public class NBTContainer extends NBTCompound {
*/
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) {

View File

@ -0,0 +1,45 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ClassWrapper;
import com.songoda.epicenchants.utils.itemnbtapi.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

@ -2,6 +2,10 @@ package com.songoda.epicenchants.utils.itemnbtapi;
import org.bukkit.entity.Entity;
import com.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion;
import com.songoda.epicenchants.utils.itemnbtapi.utils.annotations.AvaliableSince;
import com.songoda.epicenchants.utils.itemnbtapi.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
@ -19,6 +23,9 @@ public class NBTEntity extends NBTCompound {
*/
public NBTEntity(Entity entity) {
super(null, null);
if (entity == null) {
throw new NullPointerException("Entity can't be null!");
}
ent = entity;
}
@ -32,4 +39,23 @@ public class NBTEntity extends NBTCompound {
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
*/
@AvaliableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
if (hasKey("BukkitValues")) {
return getCompound("BukkitValues");
} else {
NBTContainer container = new NBTContainer();
container.addCompound("BukkitValues").setString("__nbtapi",
"Marker to make the PersistentDataContainer have content");
mergeCompound(container);
return getCompound("BukkitValues");
}
}
}

View File

@ -27,10 +27,13 @@ public class NBTFile extends NBTCompound {
*/
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.readNBTFile(inputsteam);
nbt = NBTReflectionUtil.readNBT(inputsteam);
} else {
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
save();
@ -43,13 +46,18 @@ public class NBTFile extends NBTCompound {
* @throws IOException
*/
public void save() throws IOException {
if (!file.exists()) {
file.getParentFile().mkdirs();
if (!file.createNewFile())
throw new IOException("Unable to create file at " + file.getAbsolutePath());
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();
}
FileOutputStream outStream = new FileOutputStream(file);
NBTReflectionUtil.saveNBTFile(nbt, outStream);
}
/**

View File

@ -0,0 +1,45 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ClassWrapper;
import com.songoda.epicenchants.utils.itemnbtapi.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,30 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import com.mojang.authlib.GameProfile;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ObjectCreator;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ReflectionMethod;
public class NBTGameProfile {
/**
* Convert a GameProfile to NBT. The NBT then can be modified or be stored
*
* @param profile
* @return A NBTContainer with all the GameProfile data
*/
public static NBTCompound toNBT(GameProfile profile) {
return new NBTContainer(ReflectionMethod.GAMEPROFILE_SERIALIZE.run(null, ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance(), profile));
}
/**
* Reconstructs a GameProfile from a NBTCompound
*
* @param compound Has to contain GameProfile data
* @return The reconstructed GameProfile
*/
public static GameProfile fromNBT(NBTCompound compound) {
return (GameProfile) ReflectionMethod.GAMEPROFILE_DESERIALIZE.run(null, NBTReflectionUtil.gettoCompount(compound.getCompound(), compound));
}
}

View File

@ -1,6 +1,8 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ReflectionMethod;
@ -14,6 +16,8 @@ import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ReflectionMet
public class NBTItem extends NBTCompound {
private ItemStack bukkitItem;
private boolean directApply;
private ItemStack originalSrcStack = null;
/**
* Constructor for NBTItems. The ItemStack will be cloned!
@ -21,11 +25,27 @@ public class NBTItem extends NBTCompound {
* @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) {
throw new NullPointerException("ItemStack can't be 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
@ -40,6 +60,58 @@ public class NBTItem extends NBTCompound {
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);
}
/**
* 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
*/
@ -83,4 +155,11 @@ public class NBTItem extends NBTCompound {
NBTReflectionUtil.convertNBTCompoundtoNMSItem(comp));
}
@Override
protected void saveCompound() {
if(directApply) {
applyNBT(originalSrcStack);
}
}
}

View File

@ -31,14 +31,14 @@ public abstract class NBTList<T> implements List<T> {
this.type = type;
this.listObject = list;
}
/**
* @return Name of this list-compound
*/
public String getName(){
public String getName() {
return listName;
}
/**
* @return The Compound's parent Object
*/
@ -55,6 +55,7 @@ public abstract class NBTList<T> implements List<T> {
@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 {
@ -64,12 +65,15 @@ public abstract class NBTList<T> implements List<T> {
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 {
@ -78,37 +82,48 @@ public abstract class NBTList<T> implements List<T> {
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();
}
}
@ -133,96 +148,141 @@ public abstract class NBTList<T> implements List<T> {
@Override
public boolean contains(Object o) {
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return true;
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return true;
}
return false;
} finally {
parent.getReadLock().unlock();
}
return false;
}
@Override
public int indexOf(Object o) {
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return i;
try {
parent.getReadLock().lock();
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
return i;
}
return -1;
} finally {
parent.getReadLock().unlock();
}
return -1;
}
@Override
public boolean addAll(Collection<? extends T> c) {
int size = size();
for (T ele : c) {
add(ele);
try {
parent.getWriteLock().lock();
int size = size();
for (T ele : c) {
add(ele);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
return size != size();
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
int size = size();
for (T ele : c) {
add(index++, ele);
try {
parent.getWriteLock().lock();
int size = size();
for (T ele : c) {
add(index++, ele);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
return size != size();
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object ele : c) {
if (!contains(ele))
return false;
try {
parent.getReadLock().lock();
for (Object ele : c) {
if (!contains(ele))
return false;
}
return true;
} finally {
parent.getReadLock().unlock();
}
return true;
}
@Override
public int lastIndexOf(Object o) {
int index = -1;
for (int i = 0; i < size(); i++) {
if (o.equals(get(i)))
index = i;
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();
}
return index;
}
@Override
public boolean removeAll(Collection<?> c) {
int size = size();
for (Object obj : c) {
remove(obj);
try {
parent.getWriteLock().lock();
int size = size();
for (Object obj : c) {
remove(obj);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
return size != size();
}
@Override
public boolean retainAll(Collection<?> c) {
int size = size();
for (Object obj : c) {
for (int i = 0; i < size(); i++) {
if (!obj.equals(get(i))) {
remove(i--);
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();
}
return size != size();
}
@Override
public boolean remove(Object o) {
int size = size();
int id = -1;
while ((id = indexOf(o)) != -1) {
remove(id);
try {
parent.getWriteLock().lock();
int size = size();
int id = -1;
while ((id = indexOf(o)) != -1) {
remove(id);
}
return size != size();
} finally {
parent.getWriteLock().unlock();
}
return size != size();
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
private int index = -1;
@Override
public boolean hasNext() {
@ -235,6 +295,12 @@ public abstract class NBTList<T> implements List<T> {
throw new NoSuchElementException();
return get(++index);
}
@Override
public void remove() {
NBTList.this.remove(index);
index--;
}
};
}
@ -248,7 +314,7 @@ public abstract class NBTList<T> implements List<T> {
final NBTList<T> list = this;
return new ListIterator<T>() {
int index = startIndex;
int index = startIndex - 1;
@Override
public void add(T e) {
@ -262,7 +328,7 @@ public abstract class NBTList<T> implements List<T> {
@Override
public boolean hasPrevious() {
return index > 0;
return index >= 0 && index <= size();
}
@Override
@ -279,7 +345,9 @@ public abstract class NBTList<T> implements List<T> {
@Override
public T previous() {
return get(--index);
if (!hasPrevious())
throw new NoSuchElementException("Id: " + (index - 1));
return get(index--);
}
@Override
@ -290,6 +358,7 @@ public abstract class NBTList<T> implements List<T> {
@Override
public void remove() {
list.remove(index);
index--;
}
@Override
@ -301,35 +370,60 @@ public abstract class NBTList<T> implements List<T> {
@Override
public Object[] toArray() {
Object[] ar = new Object[size()];
for (int i = 0; i < size(); i++)
ar[i] = get(i);
return ar;
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) {
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.");
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();
}
return ar;
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
ArrayList<T> list = new ArrayList<>();
for (int i = fromIndex; i < toIndex; i++)
list.add(get(i));
return list;
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,45 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ClassWrapper;
import com.songoda.epicenchants.utils.itemnbtapi.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

@ -1,14 +1,17 @@
package com.songoda.epicenchants.utils.itemnbtapi;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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.songoda.epicenchants.utils.itemnbtapi.utils.GsonWrapper;
import com.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion;
@ -25,6 +28,17 @@ import com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings.ReflectionMet
*/
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
*/
@ -47,12 +61,12 @@ public class NBTReflectionUtil {
}
/**
* Reads in a FileInputStream as NMS Compound
* Reads in a InputStream as NMS Compound
*
* @param stream InputStream of any NBT file
* @return NMS Compound
*/
public static Object readNBTFile(FileInputStream stream) {
public static Object readNBT(InputStream stream) {
try {
return ReflectionMethod.NBTFILE_READ.run(null, stream);
} catch (Exception e) {
@ -61,17 +75,38 @@ public class NBTReflectionUtil {
}
/**
* Writes a NMS Compound to a FileOutputStream
* Writes a NMS Compound to an OutputStream
*
* @param nbt NMS Compound
* @param stream Stream to write to
* @return ???
*/
public static Object saveNBTFile(Object nbt, FileOutputStream stream) {
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 saving a NBT File!", 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);
}
}
@ -99,10 +134,11 @@ public class NBTReflectionUtil {
*/
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(nbtcompound.getCompound());
return ObjectCreator.NMS_COMPOUNDFROMITEM.getInstance(nmsComp);
} else {
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nbtcompound.getCompound());
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nmsComp);
}
} catch (Exception e) {
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack!", e);
@ -124,6 +160,21 @@ public class NBTReflectionUtil {
}
}
/**
* 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
*
@ -166,10 +217,15 @@ public class NBTReflectionUtil {
*/
public static Object getTileEntityNBTTagCompound(BlockState tile) {
try {
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
Object o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
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 tag = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
Object answer = ReflectionMethod.TILEENTITY_GET_NBT.run(o, tag);
if (answer == null)
@ -188,11 +244,21 @@ public class NBTReflectionUtil {
*/
public static void setTileEntityNBTTagCompound(BlockState tile, Object comp) {
try {
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
Object o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
ReflectionMethod.TILEENTITY_SET_NBT.run(o, comp);
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.getVersion().getVersionId() >= MinecraftVersion.MC1_16_R1.getVersionId()) {
Object blockData = ReflectionMethod.TILEENTITY_GET_BLOCKDATA.run(o);
ReflectionMethod.TILEENTITY_SET_NBT.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);
}
@ -232,8 +298,9 @@ public class NBTReflectionUtil {
if (nbttag == null) {
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
}
if (!valideCompound(comp))
if (!valideCompound(comp)) {
return;
}
Object workingtag = gettoCompount(nbttag, comp);
try {
ReflectionMethod.COMPOUND_SET.run(workingtag, name,
@ -277,10 +344,10 @@ public class NBTReflectionUtil {
/**
* Merges the second {@link NBTCompound} into the first one
*
* @param comp Target for the merge
* @param nbtcompound Data to merge
* @param comp Target for the merge
* @param nbtcompoundSrc Data to merge
*/
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompound) {
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompoundSrc) {
Object rootnbttag = comp.getCompound();
if (rootnbttag == null) {
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
@ -288,8 +355,15 @@ public class NBTReflectionUtil {
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, nbtcompound.getCompound());
ReflectionMethod.COMPOUND_MERGE.run(workingtag, workingtagSrc);
comp.setCompound(rootnbttag);
} catch (Exception e) {
throw new NbtApiException("Exception while merging two NBTCompounds!", e);
@ -372,6 +446,12 @@ public class NBTReflectionUtil {
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;
}

View File

@ -2,6 +2,10 @@ package com.songoda.epicenchants.utils.itemnbtapi;
import org.bukkit.block.BlockState;
import com.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion;
import com.songoda.epicenchants.utils.itemnbtapi.utils.annotations.AvaliableSince;
import com.songoda.epicenchants.utils.itemnbtapi.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
@ -20,6 +24,9 @@ public class NBTTileEntity extends NBTCompound {
*/
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;
}
@ -33,4 +40,23 @@ public class NBTTileEntity extends NBTCompound {
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
*/
@AvaliableSince(version = MinecraftVersion.MC1_14_R1)
public NBTCompound getPersistentDataContainer() {
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

@ -1,377 +1,387 @@
package com.songoda.epicenchants.utils.itemnbtapi.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.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion.logger;
/**
* 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
private static final String PLUGINVERSION = "2.2.0-SNAPSHOT"; // In case you fork the nbt-api for internal use in your network, plugins and so on, you *may* add that to the version here. (2.x.x-Timolia or something like that?)
// Not sure how good of an idea that is, so maybe just leave it as is ¯\_()_/¯
// 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) { }
}
// Register our service
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
if (!found) {
logger.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", PLUGINVERSION); // 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) {
logger.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) {
logger.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) {
logger.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();
}
}
package com.songoda.epicenchants.utils.itemnbtapi.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.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion.logger;
/**
* 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) {
logger.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) {
logger.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) {
logger.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) {
logger.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) {
logger.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

@ -16,27 +16,22 @@ import org.bukkit.Bukkit;
@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_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);
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
*/
public static final Logger logger = Logger.getLogger("NBTAPI");
// NBT-API Version
protected static final String VERSION = "2.5.0-SNAPSHOT";
private final int versionId;
MinecraftVersion(int versionId) {
@ -49,6 +44,16 @@ public enum MinecraftVersion {
public int getVersionId() {
return versionId;
}
/**
* 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();
}
/**
* Getter for this servers MinecraftVersion. Also init's bStats and checks the
@ -78,11 +83,20 @@ public enum MinecraftVersion {
private static void init() {
try {
if (!bStatsDisabled)
if (hasGsonSupport() && !bStatsDisabled)
new ApiMetricsLite();
} catch (Exception ex) {
logger.log(Level.WARNING, "[NBTAPI] Error enabeling Metrics!", 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',
@ -128,6 +142,14 @@ public enum MinecraftVersion {
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

View File

@ -0,0 +1,106 @@
package com.songoda.epicenchants.utils.itemnbtapi.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.songoda.epicenchants.utils.itemnbtapi.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.logger.log(Level.WARNING,
"[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage() + "' seems to be outdated!");
MinecraftVersion.logger.log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
MinecraftVersion.logger.log(Level.WARNING,
"[NBTAPI] Please update the nbt-api or the plugin that contains the api!");
} else if (versionDifference == 0) {
MinecraftVersion.logger.log(Level.INFO, "[NBTAPI] The NBT-API seems to be up-to-date!");
} else if (versionDifference == 1) {
MinecraftVersion.logger.log(Level.WARNING, "[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage()
+ "' seems to be a future Version, not yet released on Spigot!");
MinecraftVersion.logger.log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
}
} else {
// wut?!
MinecraftVersion.logger.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.songoda.epicenchants.utils.itemnbtapi.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.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion;
@Retention(RUNTIME)
@Target({ METHOD })
public @interface AvaliableSince {
MinecraftVersion version();
}

View File

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

View File

@ -15,18 +15,22 @@ import static com.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion.l
@SuppressWarnings("javadoc")
public enum ClassWrapper {
CRAFT_ITEMSTACK(PackageWrapper.CRAFTBUKKIT, "inventory.CraftItemStack"),
CRAFT_METAITEM(PackageWrapper.CRAFTBUKKIT, "inventory.CraftMetaItem"),
CRAFT_ENTITY(PackageWrapper.CRAFTBUKKIT, "entity.CraftEntity"),
CRAFT_WORLD(PackageWrapper.CRAFTBUKKIT, "CraftWorld"),
NMS_NBTBASE(PackageWrapper.NMS, "NBTBase"),
NMS_NBTTAGSTRING(PackageWrapper.NMS, "NBTTagString"),
NMS_NBTTAGINT(PackageWrapper.NMS, "NBTTagInt"),
NMS_NBTTAGFLOAT(PackageWrapper.NMS, "NBTTagFloat"),
NMS_NBTTAGDOUBLE(PackageWrapper.NMS, "NBTTagDouble"),
NMS_NBTTAGLONG(PackageWrapper.NMS, "NBTTagLong"),
NMS_ITEMSTACK(PackageWrapper.NMS, "ItemStack"),
NMS_NBTTAGCOMPOUND(PackageWrapper.NMS, "NBTTagCompound"),
NMS_NBTTAGLIST(PackageWrapper.NMS, "NBTTagList"),
NMS_NBTCOMPRESSEDSTREAMTOOLS(PackageWrapper.NMS, "NBTCompressedStreamTools"),
NMS_MOJANGSONPARSER(PackageWrapper.NMS, "MojangsonParser"),
NMS_TILEENTITY(PackageWrapper.NMS, "TileEntity"),
NMS_BLOCKPOSITION(PackageWrapper.NMS, "BlockPosition"),
NMS_BLOCKPOSITION(PackageWrapper.NMS, "BlockPosition", MinecraftVersion.MC1_8_R3, null),
NMS_WORLDSERVER(PackageWrapper.NMS, "WorldServer"),
NMS_MINECRAFTSERVER(PackageWrapper.NMS, "MinecraftServer"),
NMS_WORLD(PackageWrapper.NMS, "World"),
@ -35,7 +39,11 @@ NMS_ENTITYTYPES(PackageWrapper.NMS, "EntityTypes"),
NMS_REGISTRYSIMPLE(PackageWrapper.NMS, "RegistrySimple", MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_12_R1),
NMS_REGISTRYMATERIALS(PackageWrapper.NMS, "RegistryMaterials"),
NMS_IREGISTRY(PackageWrapper.NMS, "IRegistry"),
NMS_MINECRAFTKEY(PackageWrapper.NMS, "MinecraftKey");
NMS_MINECRAFTKEY(PackageWrapper.NMS, "MinecraftKey", MinecraftVersion.MC1_8_R3, null),
NMS_GAMEPROFILESERIALIZER(PackageWrapper.NMS, "GameProfileSerializer"),
NMS_IBLOCKDATA(PackageWrapper.NMS, "IBlockData", MinecraftVersion.MC1_8_R3, null),
GAMEPROFILE("com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3)
;
private Class<?> clazz;
private boolean enabled = false;
@ -60,6 +68,18 @@ NMS_MINECRAFTKEY(PackageWrapper.NMS, "MinecraftKey");
}
}
ClassWrapper(String path, MinecraftVersion from){
if(from != null && MinecraftVersion.getVersion().getVersionId() < from.getVersionId()) {
return;
}
enabled = true;
try{
clazz = Class.forName(path);
}catch(Exception ex){
logger.log(Level.WARNING, "[NBTAPI] Error while trying to resolve the class '" + path + "'!", ex);
}
}
/**
* @return The wrapped class
*/

View File

@ -16,40 +16,41 @@ import static com.songoda.epicenchants.utils.itemnbtapi.utils.MinecraftVersion.l
*/
@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()),
;
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;
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) {
logger.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);
}
}
ObjectCreator(MinecraftVersion from, MinecraftVersion to, Class<?> clazz, Class<?>... args){
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){
logger.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

@ -8,13 +8,13 @@ package com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings;
*/
@SuppressWarnings("javadoc")
public enum PackageWrapper {
NMS("net.minecraft.server"),
CRAFTBUKKIT("org.bukkit.craftbukkit"),
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'})),
;
private final String uri;
PackageWrapper(String uri) {
private PackageWrapper(String uri) {
this.uri = uri;
}

View File

@ -1,182 +1,192 @@
package com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import org.bukkit.inventory.ItemStack;
import com.songoda.epicenchants.utils.itemnbtapi.NbtApiException;
import com.songoda.epicenchants.utils.itemnbtapi.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.getClazz(), new Class[]{String.class, float.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setFloat")),
COMPOUND_SET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setString")),
COMPOUND_SET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setInt")),
COMPOUND_SET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, byte[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByteArray")),
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray")),
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, long.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong")),
COMPOUND_SET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, short.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setShort")),
COMPOUND_SET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, byte.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByte")),
COMPOUND_SET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, double.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setDouble")),
COMPOUND_SET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, boolean.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean")),
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a")), //FIXME: No Spigot mapping!
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set")),
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get")),
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList")),
COMPOUND_GET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getFloat")),
COMPOUND_GET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString")),
COMPOUND_GET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getInt")),
COMPOUND_GET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByteArray")),
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getIntArray")),
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getLong")),
COMPOUND_GET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getShort")),
COMPOUND_GET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByte")),
COMPOUND_GET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getDouble")),
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getBoolean")),
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getCompound")),
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[] {}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTag")),
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "save")),
NMSITEM_CREATESTACK(ClassWrapper.NMS_ITEMSTACK.getClazz(), 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.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "remove")),
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "hasKey")),
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), 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")), //FIXME: No Spigot mapping!
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys")),
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys")),
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove")),
LIST_SIZE(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "size")),
LIST_SET(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_13_R1, "set")),
LEGACY_LIST_ADD(ClassWrapper.NMS_NBTTAGLIST.getClazz(), 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.getClazz(), new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "add")),
LIST_GET_STRING(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString")),
LIST_GET_COMPOUND(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get")),
LIST_GET(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "g"), new Since(MinecraftVersion.MC1_9_R1, "h"), new Since(MinecraftVersion.MC1_12_R1, "i"), new Since(MinecraftVersion.MC1_13_R1, "get")),
ITEMSTACK_SET_TAG(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTag")),
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK.getClazz(), new Class[]{ItemStack.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK.getClazz(), new Class[]{ClassWrapper.NMS_ITEMSTACK.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTileEntity")),
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTileEntity")),
NMS_WORLD_REMOVE_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "t"), new Since(MinecraftVersion.MC1_9_R1, "s"), new Since(MinecraftVersion.MC1_13_R1, "n"), new Since(MinecraftVersion.MC1_14_R1, "removeTileEntity")),
TILEENTITY_LOAD_LEGACY191(ClassWrapper.NMS_TILEENTITY.getClazz(), 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.getClazz(), 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.getClazz(), 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(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_12_R1, "create")),
TILEENTITY_GET_NBT(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "b"), new Since(MinecraftVersion.MC1_9_R1, "save")),
TILEENTITY_SET_NBT(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_12_R1, "load")),
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "f")), //FIXME: No Spigot mapping!
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "e"), new Since(MinecraftVersion.MC1_12_R1, "save")),
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{}, MinecraftVersion.MC1_14_R1,new Since(MinecraftVersion.MC1_14_R1, "getSaveID")),
NBTFILE_READ(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS.getClazz(), new Class[]{InputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a")), //FIXME: No Spigot mapping!
NBTFILE_WRITE(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), OutputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a")), //FIXME: No Spigot mapping!
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "parse")),
REGISTRY_KEYSET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), new Class[]{}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
REGISTRY_GET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "get")),
REGISTRY_SET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), 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.getClazz(), 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.getClazz(), new Class[]{}, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_13_R1, "keySet")),
REGISTRYMATERIALS_GET (ClassWrapper.NMS_REGISTRYMATERIALS.getClazz(), new Class[]{ClassWrapper.NMS_MINECRAFTKEY.getClazz()}, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_13_R1, "get")),
;
private MinecraftVersion removedAfter;
private Since targetVersion;
private Method method;
private boolean loaded = false;
private boolean compatible = false;
private String methodName = null;
ReflectionMethod(Class<?> targetClass, Class<?>[] args, MinecraftVersion addedSince, MinecraftVersion removedAfter, Since... methodnames){
this.removedAfter = removedAfter;
MinecraftVersion server = MinecraftVersion.getVersion();
if(server.compareTo(addedSince) < 0 || (this.removedAfter != null && server.getVersionId() > this.removedAfter.getVersionId()))return;
compatible = true;
Since target = methodnames[0];
for(Since s : methodnames){
if(s.version.getVersionId() <= server.getVersionId() && target.version.getVersionId() < s.version.getVersionId())
target = s;
}
targetVersion = target;
try{
method = targetClass.getMethod(targetVersion.name, args);
method.setAccessible(true);
loaded = true;
methodName = targetVersion.name;
}catch(NullPointerException | NoSuchMethodException | SecurityException ex){
System.out.println("[NBTAPI] Unable to find the method '" + targetVersion.name + "' in '" + targetClass.getSimpleName() + "' Enum: " + this); //NOSONAR This gets loaded before the logger is loaded
}
}
ReflectionMethod(Class<?> 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, 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;
}
protected static class Since{
public final MinecraftVersion version;
public final String name;
public Since(MinecraftVersion version, String name) {
this.version = version;
this.name = name;
}
}
}
package com.songoda.epicenchants.utils.itemnbtapi.utils.nmsmappings;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.UUID;
import org.bukkit.inventory.ItemStack;
import com.songoda.epicenchants.utils.itemnbtapi.NbtApiException;
import com.songoda.epicenchants.utils.itemnbtapi.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.getClazz(), new Class[]{String.class, float.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setFloat")),
COMPOUND_SET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setString")),
COMPOUND_SET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setInt")),
COMPOUND_SET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, byte[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByteArray")),
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray")),
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, long.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong")),
COMPOUND_SET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, short.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setShort")),
COMPOUND_SET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, byte.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByte")),
COMPOUND_SET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, double.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setDouble")),
COMPOUND_SET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, boolean.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean")),
COMPOUND_SET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, UUID.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a")),
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a")), //FIXME: No Spigot mapping!
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set")),
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get")),
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList")),
COMPOUND_OWN_TYPE(ClassWrapper.NMS_NBTBASE.getClazz(), 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.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getFloat")),
COMPOUND_GET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString")),
COMPOUND_GET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getInt")),
COMPOUND_GET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByteArray")),
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getIntArray")),
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getLong")),
COMPOUND_GET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getShort")),
COMPOUND_GET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByte")),
COMPOUND_GET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getDouble")),
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getBoolean")),
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a")),
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getCompound")),
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[] {}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTag")),
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "save")),
NMSITEM_CREATESTACK(ClassWrapper.NMS_ITEMSTACK.getClazz(), 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.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "remove")),
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "hasKey")),
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), 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")), //FIXME: No Spigot mapping!
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys")),
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys")),
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove")),
LIST_SIZE(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "size")),
LIST_SET(ClassWrapper.NMS_NBTTAGLIST.getClazz(), 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")),
LEGACY_LIST_ADD(ClassWrapper.NMS_NBTTAGLIST.getClazz(), 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.getClazz(), new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "add")),
LIST_GET_STRING(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString")),
LIST_GET_COMPOUND(ClassWrapper.NMS_NBTTAGLIST.getClazz(), new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get")),
LIST_GET(ClassWrapper.NMS_NBTTAGLIST.getClazz(), 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")),
ITEMSTACK_SET_TAG(ClassWrapper.NMS_ITEMSTACK.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTag")),
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK.getClazz(), new Class[]{ItemStack.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK.getClazz(), new Class[]{ClassWrapper.NMS_ITEMSTACK.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "getTileEntity")),
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "setTileEntity")),
NMS_WORLD_REMOVE_TILEENTITY(ClassWrapper.NMS_WORLDSERVER.getClazz(), new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, 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.getClazz(), 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.getClazz(), 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.getClazz(), 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.getClazz(), 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.getClazz(), 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.getClazz(), new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "create")),
TILEENTITY_GET_NBT(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "b"), new Since(MinecraftVersion.MC1_9_R1, "save")),
TILEENTITY_SET_NBT_LEGACY1151(ClassWrapper.NMS_TILEENTITY.getClazz(), 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(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "load")),
TILEENTITY_GET_BLOCKDATA(ClassWrapper.NMS_TILEENTITY.getClazz(), new Class[]{}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "getBlock")),
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY.getClazz(), new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "f"), new Since(MinecraftVersion.MC1_16_R1, "load")), //FIXME: No Spigot mapping!
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "e"), new Since(MinecraftVersion.MC1_12_R1, "save")),
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY.getClazz(), new Class[]{}, MinecraftVersion.MC1_14_R1,new Since(MinecraftVersion.MC1_14_R1, "getSaveID")),
NBTFILE_READ(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS.getClazz(), new Class[]{InputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a")), //FIXME: No Spigot mapping!
NBTFILE_WRITE(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), OutputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a")), //FIXME: No Spigot mapping!
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER.getClazz(), new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "parse")),
REGISTRY_KEYSET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), new Class[]{}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
REGISTRY_GET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "get")),
REGISTRY_SET (ClassWrapper.NMS_REGISTRYSIMPLE.getClazz(), 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.getClazz(), 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.getClazz(), new Class[]{}, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_13_R1, "keySet")),
REGISTRYMATERIALS_GET (ClassWrapper.NMS_REGISTRYMATERIALS.getClazz(), new Class[]{ClassWrapper.NMS_MINECRAFTKEY.getClazz()}, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_13_R1, "get")),
REGISTRYMATERIALS_GETKEY (ClassWrapper.NMS_REGISTRYMATERIALS.getClazz(), new Class[]{Object.class}, MinecraftVersion.MC1_13_R2, new Since(MinecraftVersion.MC1_13_R2, "getKey")),
GAMEPROFILE_DESERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "deserialize")),
GAMEPROFILE_SERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER.getClazz(), new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.GAMEPROFILE.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "serialize"));
private MinecraftVersion removedAfter;
private Since targetVersion;
private Method method;
private boolean loaded = false;
private boolean compatible = false;
private String methodName = null;
ReflectionMethod(Class<?> targetClass, Class<?>[] args, MinecraftVersion addedSince, MinecraftVersion removedAfter, Since... methodnames){
this.removedAfter = removedAfter;
MinecraftVersion server = MinecraftVersion.getVersion();
if(server.compareTo(addedSince) < 0 || (this.removedAfter != null && server.getVersionId() > this.removedAfter.getVersionId()))return;
compatible = true;
Since target = methodnames[0];
for(Since s : methodnames){
if(s.version.getVersionId() <= server.getVersionId() && target.version.getVersionId() < s.version.getVersionId())
target = s;
}
targetVersion = target;
try{
method = targetClass.getMethod(targetVersion.name, args);
method.setAccessible(true);
loaded = true;
methodName = targetVersion.name;
}catch(NullPointerException | NoSuchMethodException | SecurityException ex){
System.out.println("[NBTAPI] Unable to find the method '" + targetVersion.name + "' in '" + targetClass.getSimpleName() + "' Enum: " + this); //NOSONAR This gets loaded before the logger is loaded
}
}
ReflectionMethod(Class<?> 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, 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;
}
protected static class Since{
public final MinecraftVersion version;
public final String name;
public Since(MinecraftVersion version, String name) {
this.version = version;
this.name = name;
}
}
}