parent
364466b665
commit
4198b2f55b
|
@ -0,0 +1,982 @@
|
|||
package net.aminecraftdev.custombosses.utils.factory;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.InputSupplier;
|
||||
import com.google.common.io.OutputSupplier;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* Created by Charles on 7/3/2017.
|
||||
*/
|
||||
public class NbtFactory {
|
||||
// Convert between NBT id and the equivalent class in java
|
||||
private static final BiMap<Integer, Class<?>> NBT_CLASS = HashBiMap.create();
|
||||
private static final BiMap<Integer, NbtType> NBT_ENUM = HashBiMap.create();
|
||||
|
||||
/**
|
||||
* Whether or not to enable stream compression.
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum StreamOptions {
|
||||
NO_COMPRESSION,
|
||||
GZIP_COMPRESSION,
|
||||
}
|
||||
|
||||
private enum NbtType {
|
||||
TAG_END(0, Void.class),
|
||||
TAG_BYTE(1, byte.class),
|
||||
TAG_SHORT(2, short.class),
|
||||
TAG_INT(3, int.class),
|
||||
TAG_LONG(4, long.class),
|
||||
TAG_FLOAT(5, float.class),
|
||||
TAG_DOUBLE(6, double.class),
|
||||
TAG_BYTE_ARRAY(7, byte[].class),
|
||||
TAG_INT_ARRAY(11, int[].class),
|
||||
TAG_STRING(8, String.class),
|
||||
TAG_LIST(9, List.class),
|
||||
TAG_COMPOUND(10, Map.class);
|
||||
|
||||
// Unique NBT id
|
||||
public final int id;
|
||||
|
||||
private NbtType(int id, Class<?> type) {
|
||||
this.id = id;
|
||||
NBT_CLASS.put(id, type);
|
||||
NBT_ENUM.put(id, this);
|
||||
}
|
||||
|
||||
private String getFieldName() {
|
||||
if (this == TAG_COMPOUND)
|
||||
return "map";
|
||||
else if (this == TAG_LIST)
|
||||
return "list";
|
||||
else
|
||||
return "data";
|
||||
}
|
||||
}
|
||||
|
||||
// The NBT base class
|
||||
private Class<?> BASE_CLASS;
|
||||
private Class<?> COMPOUND_CLASS;
|
||||
private Class<?> STREAM_TOOLS;
|
||||
private Class<?> READ_LIMITER_CLASS;
|
||||
private Method NBT_CREATE_TAG;
|
||||
private Method NBT_GET_TYPE;
|
||||
private Field NBT_LIST_TYPE;
|
||||
private final Field[] DATA_FIELD = new Field[12];
|
||||
|
||||
// CraftItemStack
|
||||
private Class<?> CRAFT_STACK;
|
||||
private Field CRAFT_HANDLE;
|
||||
private Field STACK_TAG;
|
||||
|
||||
// Loading/saving compounds
|
||||
private LoadCompoundMethod LOAD_COMPOUND;
|
||||
private Method SAVE_COMPOUND;
|
||||
|
||||
// Shared instance
|
||||
private static NbtFactory INSTANCE;
|
||||
|
||||
/**
|
||||
* Represents a root NBT compound.
|
||||
* <p>
|
||||
* All changes to this map will be reflected in the underlying NBT compound. Values may only be one of the following:
|
||||
* <ul>
|
||||
* <li>Primitive types</li>
|
||||
* <li>{@link String String}</li>
|
||||
* <li>{@link NbtList}</li>
|
||||
* <li>{@link NbtCompound}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* See also:
|
||||
* <ul>
|
||||
* <li>{@link NbtFactory#createCompound()}</li>
|
||||
* <li>{@link NbtFactory#fromCompound(Object)}</li>
|
||||
* </ul>
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class NbtCompound extends ConvertedMap {
|
||||
private NbtCompound(Object handle) {
|
||||
super(handle, getDataMap(handle));
|
||||
}
|
||||
|
||||
// Simplifiying access to each value
|
||||
public Byte getByte(String key, Byte defaultValue) {
|
||||
return containsKey(key) ? (Byte)get(key) : defaultValue;
|
||||
}
|
||||
public Short getShort(String key, Short defaultValue) {
|
||||
return containsKey(key) ? (Short)get(key) : defaultValue;
|
||||
}
|
||||
public Integer getInteger(String key, Integer defaultValue) {
|
||||
return containsKey(key) ? (Integer)get(key) : defaultValue;
|
||||
}
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
return containsKey(key) ? (Long)get(key) : defaultValue;
|
||||
}
|
||||
public Float getFloat(String key, Float defaultValue) {
|
||||
return containsKey(key) ? (Float)get(key) : defaultValue;
|
||||
}
|
||||
public Double getDouble(String key, Double defaultValue) {
|
||||
return containsKey(key) ? (Double)get(key) : defaultValue;
|
||||
}
|
||||
public String getString(String key, String defaultValue) {
|
||||
return containsKey(key) ? (String)get(key) : defaultValue;
|
||||
}
|
||||
public byte[] getByteArray(String key, byte[] defaultValue) {
|
||||
return containsKey(key) ? (byte[])get(key) : defaultValue;
|
||||
}
|
||||
public int[] getIntegerArray(String key, int[] defaultValue) {
|
||||
return containsKey(key) ? (int[])get(key) : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the list by the given name.
|
||||
* @param key - the name of the list.
|
||||
* @param createNew - whether or not to create a new list if its missing.
|
||||
* @return An existing list, a new list or NULL.
|
||||
*/
|
||||
public NbtList getList(String key, boolean createNew) {
|
||||
NbtList list = (NbtList) get(key);
|
||||
|
||||
if (list == null && createNew)
|
||||
put(key, list = createList());
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the map by the given name.
|
||||
* @param key - the name of the map.
|
||||
* @param createNew - whether or not to create a new map if its missing.
|
||||
* @return An existing map, a new map or NULL.
|
||||
*/
|
||||
public NbtCompound getMap(String key, boolean createNew) {
|
||||
return getMap(Arrays.asList(key), createNew);
|
||||
}
|
||||
// Done
|
||||
|
||||
/**
|
||||
* Set the value of an entry at a given location.
|
||||
* <p>
|
||||
* Every element of the path (except the end) are assumed to be compounds, and will
|
||||
* be created if they are missing.
|
||||
* @param path - the path to the entry.
|
||||
* @param value - the new value of this entry.
|
||||
* @return This compound, for chaining.
|
||||
*/
|
||||
public NbtCompound putPath(String path, Object value) {
|
||||
List<String> entries = getPathElements(path);
|
||||
Map<String, Object> map = getMap(entries.subList(0, entries.size() - 1), true);
|
||||
|
||||
map.put(entries.get(entries.size() - 1), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value of a given entry in the tree.
|
||||
* <p>
|
||||
* Every element of the path (except the end) are assumed to be compounds. The
|
||||
* retrieval operation will be cancelled if any of them are missing.
|
||||
* @param path - path to the entry.
|
||||
* @return The value, or NULL if not found.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getPath(String path) {
|
||||
List<String> entries = getPathElements(path);
|
||||
NbtCompound map = getMap(entries.subList(0, entries.size() - 1), false);
|
||||
|
||||
if (map != null) {
|
||||
return (T) map.get(entries.get(entries.size() - 1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the content of a NBT compound to a stream.
|
||||
* <p>
|
||||
* Use {@link Files#newOutputStreamSupplier(File)} to provide a stream supplier to a file.
|
||||
* @param stream - the output stream.
|
||||
* @param option - whether or not to compress the output.
|
||||
* @throws IOException If anything went wrong.
|
||||
*/
|
||||
public void saveTo(OutputSupplier<? extends OutputStream> stream, StreamOptions option) throws IOException {
|
||||
saveStream(this, stream, option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a map from a given path.
|
||||
* @param path - path of compounds to look up.
|
||||
* @param createNew - whether or not to create new compounds on the way.
|
||||
* @return The map at this location.
|
||||
*/
|
||||
private NbtCompound getMap(Iterable<String> path, boolean createNew) {
|
||||
NbtCompound current = this;
|
||||
|
||||
for (String entry : path) {
|
||||
NbtCompound child = (NbtCompound) current.get(entry);
|
||||
|
||||
if (child == null) {
|
||||
if (!createNew)
|
||||
return null;
|
||||
current.put(entry, child = createCompound());
|
||||
}
|
||||
current = child;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the path into separate elements.
|
||||
* @param path - the path to split.
|
||||
* @return The elements.
|
||||
*/
|
||||
private List<String> getPathElements(String path) {
|
||||
return Lists.newArrayList(Splitter.on(".").omitEmptyStrings().split(path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a root NBT list.
|
||||
* See also:
|
||||
* <ul>
|
||||
* <li>{@link NbtFactory#fromList(Object)}</li>
|
||||
* </ul>
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class NbtList extends ConvertedList {
|
||||
private NbtList(Object handle) {
|
||||
super(handle, getDataList(handle));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an object that provides a view of a native NMS class.
|
||||
* @author Kristian
|
||||
*/
|
||||
public static interface Wrapper {
|
||||
/**
|
||||
* Retrieve the underlying native NBT tag.
|
||||
* @return The underlying NBT.
|
||||
*/
|
||||
public Object getHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or construct a shared NBT factory.
|
||||
* @return The factory.
|
||||
*/
|
||||
private static NbtFactory get() {
|
||||
if (INSTANCE == null)
|
||||
INSTANCE = new NbtFactory();
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance of the NBT factory by deducing the class of NBTBase.
|
||||
*/
|
||||
private NbtFactory() {
|
||||
if (BASE_CLASS == null) {
|
||||
try {
|
||||
// Keep in mind that I do use hard-coded field names - but it's okay as long as we're dealing
|
||||
// with CraftBukkit or its derivatives. This does not work in MCPC+ however.
|
||||
ClassLoader loader = NbtFactory.class.getClassLoader();
|
||||
|
||||
String packageName = getPackageName();
|
||||
Class<?> offlinePlayer = loader.loadClass(packageName + ".CraftOfflinePlayer");
|
||||
|
||||
// Prepare NBT
|
||||
COMPOUND_CLASS = getMethod(0, Modifier.STATIC, offlinePlayer, "getData").getReturnType();
|
||||
BASE_CLASS = COMPOUND_CLASS.getSuperclass();
|
||||
NBT_GET_TYPE = getMethod(0, Modifier.STATIC, BASE_CLASS, "getTypeId");
|
||||
NBT_CREATE_TAG = getMethod(Modifier.STATIC, 0, BASE_CLASS, "createTag", byte.class);
|
||||
|
||||
// Prepare CraftItemStack
|
||||
CRAFT_STACK = loader.loadClass(packageName + ".inventory.CraftItemStack");
|
||||
CRAFT_HANDLE = getField(null, CRAFT_STACK, "handle");
|
||||
STACK_TAG = getField(null, CRAFT_HANDLE.getType(), "tag");
|
||||
|
||||
// Loading/saving
|
||||
String nmsPackage = BASE_CLASS.getPackage().getName();
|
||||
initializeNMS(loader, nmsPackage);
|
||||
|
||||
LOAD_COMPOUND = READ_LIMITER_CLASS != null ?
|
||||
new LoadMethodSkinUpdate(STREAM_TOOLS, READ_LIMITER_CLASS) :
|
||||
new LoadMethodWorldUpdate(STREAM_TOOLS);
|
||||
SAVE_COMPOUND = getMethod(Modifier.STATIC, 0, STREAM_TOOLS, null, BASE_CLASS, DataOutput.class);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Unable to find offline player.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeNMS(ClassLoader loader, String nmsPackage) {
|
||||
try {
|
||||
STREAM_TOOLS = loader.loadClass(nmsPackage + ".NBTCompressedStreamTools");
|
||||
READ_LIMITER_CLASS = loader.loadClass(nmsPackage + ".NBTReadLimiter");
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Ignore - we will detect this later
|
||||
}
|
||||
}
|
||||
|
||||
private String getPackageName() {
|
||||
Server server = Bukkit.getServer();
|
||||
String name = server != null ? server.getClass().getPackage().getName() : null;
|
||||
|
||||
if (name != null && name.contains("craftbukkit")) {
|
||||
return name;
|
||||
} else {
|
||||
// Fallback
|
||||
return "org.bukkit.craftbukkit.v1_7_R3";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> getDataMap(Object handle) {
|
||||
return (Map<String, Object>) getFieldValue(
|
||||
getDataField(NbtType.TAG_COMPOUND, handle), handle);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> getDataList(Object handle) {
|
||||
return (List<Object>) getFieldValue(
|
||||
getDataField(NbtType.TAG_LIST, handle), handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NBT list of an unspecified type.
|
||||
* @return The NBT list.
|
||||
*/
|
||||
public static NbtList createList(Object... content) {
|
||||
return createList(Arrays.asList(content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NBT list of an unspecified type.
|
||||
* @return The NBT list.
|
||||
*/
|
||||
public static NbtList createList(Iterable<? extends Object> iterable) {
|
||||
NbtList list = get().new NbtList(
|
||||
INSTANCE.createNbtTag(NbtType.TAG_LIST, null)
|
||||
);
|
||||
|
||||
// Add the content as well
|
||||
for (Object obj : iterable)
|
||||
list.add(obj);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NBT compound.
|
||||
* <p>
|
||||
* @return The NBT compound.
|
||||
*/
|
||||
public static NbtCompound createCompound() {
|
||||
return get().new NbtCompound(
|
||||
INSTANCE.createNbtTag(NbtType.TAG_COMPOUND, null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NBT wrapper from a list.
|
||||
* @param nmsList - the NBT list.
|
||||
* @return The wrapper.
|
||||
*/
|
||||
public static NbtList fromList(Object nmsList) {
|
||||
return get().new NbtList(nmsList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the content of a file from a stream.
|
||||
* <p>
|
||||
* Use {@link Files#newInputStreamSupplier(File)} to provide a stream from a file.
|
||||
* @param stream - the stream supplier.
|
||||
* @param option - whether or not to decompress the input stream.
|
||||
* @return The decoded NBT compound.
|
||||
* @throws IOException If anything went wrong.
|
||||
*/
|
||||
public static NbtCompound fromStream(InputSupplier<? extends InputStream> stream, StreamOptions option) throws IOException {
|
||||
InputStream input = null;
|
||||
DataInputStream data = null;
|
||||
boolean suppress = true;
|
||||
|
||||
try {
|
||||
input = stream.getInput();
|
||||
data = new DataInputStream(new BufferedInputStream(
|
||||
option == StreamOptions.GZIP_COMPRESSION ? new GZIPInputStream(input) : input
|
||||
));
|
||||
|
||||
NbtCompound result = fromCompound(get().LOAD_COMPOUND.loadNbt(data));
|
||||
suppress = false;
|
||||
return result;
|
||||
|
||||
} finally {
|
||||
if (data != null)
|
||||
Closeables.close(data, suppress);
|
||||
else if (input != null)
|
||||
Closeables.close(input, suppress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the content of a NBT compound to a stream.
|
||||
* <p>
|
||||
* Use {@link Files#newOutputStreamSupplier(File)} to provide a stream supplier to a file.
|
||||
* @param source - the NBT compound to save.
|
||||
* @param stream - the stream.
|
||||
* @param option - whether or not to compress the output.
|
||||
* @throws IOException If anything went wrong.
|
||||
*/
|
||||
public static void saveStream(NbtCompound source, OutputSupplier<? extends OutputStream> stream, StreamOptions option) throws IOException {
|
||||
OutputStream output = null;
|
||||
DataOutputStream data = null;
|
||||
boolean suppress = true;
|
||||
|
||||
try {
|
||||
output = stream.getOutput();
|
||||
data = new DataOutputStream(
|
||||
option == StreamOptions.GZIP_COMPRESSION ? new GZIPOutputStream(output) : output
|
||||
);
|
||||
|
||||
invokeMethod(get().SAVE_COMPOUND, null, source.getHandle(), data);
|
||||
suppress = false;
|
||||
|
||||
} finally {
|
||||
if (data != null)
|
||||
Closeables.close(data, suppress);
|
||||
else if (output != null)
|
||||
Closeables.close(output, suppress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NBT wrapper from a compound.
|
||||
* @param nmsCompound - the NBT compund.
|
||||
* @return The wrapper.
|
||||
*/
|
||||
public static NbtCompound fromCompound(Object nmsCompound) {
|
||||
return get().new NbtCompound(nmsCompound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the NBT compound tag of a given item stack.
|
||||
* <p>
|
||||
* The item stack must be a wrapper for a CraftItemStack. Use
|
||||
* @param stack - the item stack, cannot be air.
|
||||
* @param compound - the new NBT compound, or NULL to remove it.
|
||||
* @throws IllegalArgumentException If the stack is not a CraftItemStack, or it represents air.
|
||||
*/
|
||||
public static void setItemTag(ItemStack stack, NbtCompound compound) {
|
||||
checkItemStack(stack);
|
||||
Object nms = getFieldValue(get().CRAFT_HANDLE, stack);
|
||||
|
||||
// Now update the tag compound
|
||||
setFieldValue(get().STACK_TAG, nms, compound.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a wrapper for an NBT tag stored (in memory) in an item stack. This is where
|
||||
* auxillary data such as enchanting, name and lore is stored. It does not include items
|
||||
* material, damage value or count.
|
||||
* <p>
|
||||
* The item stack must be a wrapper for a CraftItemStack.
|
||||
* @param stack - the item stack.
|
||||
* @return A wrapper for its NBT tag.
|
||||
*/
|
||||
public static NbtCompound fromItemTag(ItemStack stack) {
|
||||
checkItemStack(stack);
|
||||
Object nms = getFieldValue(get().CRAFT_HANDLE, stack);
|
||||
Object tag = getFieldValue(get().STACK_TAG, nms);
|
||||
|
||||
// Create the tag if it doesn't exist
|
||||
if (tag == null) {
|
||||
NbtCompound compound = createCompound();
|
||||
setItemTag(stack, compound);
|
||||
return compound;
|
||||
}
|
||||
return fromCompound(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a CraftItemStack version of the stack.
|
||||
* @param stack - the stack to convert.
|
||||
* @return The CraftItemStack version.
|
||||
*/
|
||||
public static ItemStack getCraftItemStack(ItemStack stack) {
|
||||
// Any need to convert?
|
||||
if (stack == null || get().CRAFT_STACK.isAssignableFrom(stack.getClass()))
|
||||
return stack;
|
||||
try {
|
||||
// Call the private constructor
|
||||
Constructor<?> caller = INSTANCE.CRAFT_STACK.getDeclaredConstructor(ItemStack.class);
|
||||
caller.setAccessible(true);
|
||||
return (ItemStack) caller.newInstance(stack);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to convert " + stack + " + to a CraftItemStack.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the given stack can store arbitrary NBT information.
|
||||
* @param stack - the stack to check.
|
||||
*/
|
||||
private static void checkItemStack(ItemStack stack) {
|
||||
if (stack == null)
|
||||
throw new IllegalArgumentException("Stack cannot be NULL.");
|
||||
if (!get().CRAFT_STACK.isAssignableFrom(stack.getClass()))
|
||||
throw new IllegalArgumentException("Stack must be a CraftItemStack.");
|
||||
if (stack.getType() == Material.AIR)
|
||||
throw new IllegalArgumentException("ItemStacks representing air cannot store NMS information.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert wrapped List and Map objects into their respective NBT counterparts.
|
||||
* @param value - the value of the element to create. Can be a List or a Map.
|
||||
* @return The NBT element.
|
||||
*/
|
||||
private Object unwrapValue(Object value) {
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
if (value instanceof Wrapper) {
|
||||
return ((Wrapper) value).getHandle();
|
||||
|
||||
} else if (value instanceof List) {
|
||||
throw new IllegalArgumentException("Can only insert a WrappedList.");
|
||||
} else if (value instanceof Map) {
|
||||
throw new IllegalArgumentException("Can only insert a WrappedCompound.");
|
||||
|
||||
} else {
|
||||
return createNbtTag(getPrimitiveType(value), value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given NBT element to a primitive wrapper or List/Map equivalent.
|
||||
* <p>
|
||||
* All changes to any mutable objects will be reflected in the underlying NBT element(s).
|
||||
* @param nms - the NBT element.
|
||||
* @return The wrapper equivalent.
|
||||
*/
|
||||
private Object wrapNative(Object nms) {
|
||||
if (nms == null)
|
||||
return null;
|
||||
|
||||
if (BASE_CLASS.isAssignableFrom(nms.getClass())) {
|
||||
final NbtType type = getNbtType(nms);
|
||||
|
||||
// Handle the different types
|
||||
switch (type) {
|
||||
case TAG_COMPOUND:
|
||||
return new NbtCompound(nms);
|
||||
case TAG_LIST:
|
||||
return new NbtList(nms);
|
||||
default:
|
||||
return getFieldValue(getDataField(type, nms), nms);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected type: " + nms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new NMS NBT tag initialized with the given value.
|
||||
* @param type - the NBT type.
|
||||
* @param value - the value, or NULL to keep the original value.
|
||||
* @return The created tag.
|
||||
*/
|
||||
private Object createNbtTag(NbtType type, Object value) {
|
||||
Object tag = invokeMethod(NBT_CREATE_TAG, null, (byte)type.id);
|
||||
|
||||
if (value != null) {
|
||||
setFieldValue(getDataField(type, tag), tag, value);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the field where the NBT class stores its value.
|
||||
* @param type - the NBT type.
|
||||
* @param nms - the NBT class instance.
|
||||
* @return The corresponding field.
|
||||
*/
|
||||
private Field getDataField(NbtType type, Object nms) {
|
||||
if (DATA_FIELD[type.id] == null)
|
||||
DATA_FIELD[type.id] = getField(nms, null, type.getFieldName());
|
||||
return DATA_FIELD[type.id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NBT type from a given NMS NBT tag.
|
||||
* @param nms - the native NBT tag.
|
||||
* @return The corresponding type.
|
||||
*/
|
||||
private NbtType getNbtType(Object nms) {
|
||||
int type = (Byte) invokeMethod(NBT_GET_TYPE, nms);
|
||||
return NBT_ENUM.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the nearest NBT type for a given primitive type.
|
||||
* @param primitive - the primitive type.
|
||||
* @return The corresponding type.
|
||||
*/
|
||||
private NbtType getPrimitiveType(Object primitive) {
|
||||
NbtType type = NBT_ENUM.get(NBT_CLASS.inverse().get(
|
||||
Primitives.unwrap(primitive.getClass())
|
||||
));
|
||||
|
||||
// Display the illegal value at least
|
||||
if (type == null)
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Illegal type: %s (%s)", primitive.getClass(), primitive));
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a method on the given target instance using the provided parameters.
|
||||
* @param method - the method to invoke.
|
||||
* @param target - the target.
|
||||
* @param params - the parameters to supply.
|
||||
* @return The result of the method.
|
||||
*/
|
||||
private static Object invokeMethod(Method method, Object target, Object... params) {
|
||||
try {
|
||||
return method.invoke(target, params);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to invoke method " + method + " for " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setFieldValue(Field field, Object target, Object value) {
|
||||
try {
|
||||
field.set(target, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to set " + field + " for " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getFieldValue(Field field, Object target) {
|
||||
try {
|
||||
return field.get(target);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to retrieve " + field + " for " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined method of the given name and parameter count.
|
||||
* @param requireMod - modifiers that are required.
|
||||
* @param bannedMod - modifiers that are banned.
|
||||
* @param clazz - a class to start with.
|
||||
* @param methodName - the method name, or NULL to skip.
|
||||
* @param params - the expected parameters.
|
||||
* @return The first method by this name.
|
||||
* @throws IllegalStateException If we cannot find this method.
|
||||
*/
|
||||
private static Method getMethod(int requireMod, int bannedMod, Class<?> clazz, String methodName, Class<?>... params) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
// Limitation: Doesn't handle overloads
|
||||
if ((method.getModifiers() & requireMod) == requireMod &&
|
||||
(method.getModifiers() & bannedMod) == 0 &&
|
||||
(methodName == null || method.getName().equals(methodName)) &&
|
||||
Arrays.equals(method.getParameterTypes(), params)) {
|
||||
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
// Search in every superclass
|
||||
if (clazz.getSuperclass() != null)
|
||||
return getMethod(requireMod, bannedMod, clazz.getSuperclass(), methodName, params);
|
||||
throw new IllegalStateException(String.format(
|
||||
"Unable to find method %s (%s).", methodName, Arrays.asList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first publically and privately defined field of the given name.
|
||||
* @param instance - an instance of the class with the field.
|
||||
* @param clazz - an optional class to start with, or NULL to deduce it from instance.
|
||||
* @param fieldName - the field name.
|
||||
* @return The first field by this name.
|
||||
* @throws IllegalStateException If we cannot find this field.
|
||||
*/
|
||||
private static Field getField(Object instance, Class<?> clazz, String fieldName) {
|
||||
if (clazz == null)
|
||||
clazz = instance.getClass();
|
||||
// Ignore access rules
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
// Recursively fild the correct field
|
||||
if (clazz.getSuperclass() != null)
|
||||
return getField(instance, clazz.getSuperclass(), fieldName);
|
||||
throw new IllegalStateException("Unable to find field " + fieldName + " in " + instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a class for caching wrappers.
|
||||
* @author Kristian
|
||||
*/
|
||||
private final class CachedNativeWrapper {
|
||||
// Don't recreate wrapper objects
|
||||
private final ConcurrentMap<Object, Object> cache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
public Object wrap(Object value) {
|
||||
Object current = cache.get(value);
|
||||
|
||||
if (current == null) {
|
||||
current = wrapNative(value);
|
||||
|
||||
// Only cache composite objects
|
||||
if (current instanceof ConvertedMap ||
|
||||
current instanceof ConvertedList) {
|
||||
cache.put(value, current);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a map that wraps another map and automatically
|
||||
* converts entries of its type and another exposed type.
|
||||
* @author Kristian
|
||||
*/
|
||||
private class ConvertedMap extends AbstractMap<String, Object> implements Wrapper {
|
||||
private final Object handle;
|
||||
private final Map<String, Object> original;
|
||||
|
||||
private final CachedNativeWrapper cache = new CachedNativeWrapper();
|
||||
|
||||
public ConvertedMap(Object handle, Map<String, Object> original) {
|
||||
this.handle = handle;
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
// For converting back and forth
|
||||
protected Object wrapOutgoing(Object value) {
|
||||
return cache.wrap(value);
|
||||
}
|
||||
protected Object unwrapIncoming(Object wrapped) {
|
||||
return unwrapValue(wrapped);
|
||||
}
|
||||
|
||||
// Modification
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
return wrapOutgoing(original.put(
|
||||
(String) key,
|
||||
unwrapIncoming(value)
|
||||
));
|
||||
}
|
||||
|
||||
// Performance
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return wrapOutgoing(original.get(key));
|
||||
}
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
return wrapOutgoing(original.remove(key));
|
||||
}
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return original.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return new AbstractSet<Entry<String,Object>>() {
|
||||
@Override
|
||||
public boolean add(Entry<String, Object> e) {
|
||||
String key = e.getKey();
|
||||
Object value = e.getValue();
|
||||
|
||||
original.put(key, unwrapIncoming(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return original.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String, Object>> iterator() {
|
||||
return ConvertedMap.this.iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Iterator<Entry<String, Object>> iterator() {
|
||||
final Iterator<Entry<String, Object>> proxy = original.entrySet().iterator();
|
||||
|
||||
return new Iterator<Entry<String, Object>>() {
|
||||
public boolean hasNext() {
|
||||
return proxy.hasNext();
|
||||
}
|
||||
|
||||
public Entry<String, Object> next() {
|
||||
Entry<String, Object> entry = proxy.next();
|
||||
|
||||
return new SimpleEntry<String, Object>(
|
||||
entry.getKey(), wrapOutgoing(entry.getValue())
|
||||
);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
proxy.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a list that wraps another list and converts elements
|
||||
* of its type and another exposed type.
|
||||
* @author Kristian
|
||||
*/
|
||||
private class ConvertedList extends AbstractList<Object> implements Wrapper {
|
||||
private final Object handle;
|
||||
|
||||
private final List<Object> original;
|
||||
private final CachedNativeWrapper cache = new CachedNativeWrapper();
|
||||
|
||||
public ConvertedList(Object handle, List<Object> original) {
|
||||
if (NBT_LIST_TYPE == null)
|
||||
NBT_LIST_TYPE = getField(handle, null, "type");
|
||||
this.handle = handle;
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
protected Object wrapOutgoing(Object value) {
|
||||
return cache.wrap(value);
|
||||
}
|
||||
protected Object unwrapIncoming(Object wrapped) {
|
||||
return unwrapValue(wrapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
return wrapOutgoing(original.get(index));
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return original.size();
|
||||
}
|
||||
@Override
|
||||
public Object set(int index, Object element) {
|
||||
return wrapOutgoing(
|
||||
original.set(index, unwrapIncoming(element))
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public void add(int index, Object element) {
|
||||
Object nbt = unwrapIncoming(element);
|
||||
|
||||
// Set the list type if its the first element
|
||||
if (size() == 0)
|
||||
setFieldValue(NBT_LIST_TYPE, handle, (byte)getNbtType(nbt).id);
|
||||
original.add(index, nbt);
|
||||
}
|
||||
@Override
|
||||
public Object remove(int index) {
|
||||
return wrapOutgoing(original.remove(index));
|
||||
}
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return original.remove(unwrapIncoming(o));
|
||||
}
|
||||
|
||||
public Object getHandle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a method for loading an NBT compound.
|
||||
* @author Kristian
|
||||
*/
|
||||
private static abstract class LoadCompoundMethod {
|
||||
protected Method staticMethod;
|
||||
|
||||
protected void setMethod(Method method) {
|
||||
this.staticMethod = method;
|
||||
this.staticMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from a given stream.
|
||||
* @param input - the input stream.
|
||||
* @return The loaded NBT compound.
|
||||
*/
|
||||
public abstract Object loadNbt(DataInput input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.2 - 1.7.5
|
||||
*/
|
||||
private static class LoadMethodWorldUpdate extends LoadCompoundMethod {
|
||||
public LoadMethodWorldUpdate(Class<?> streamClass) {
|
||||
setMethod(getMethod(Modifier.STATIC, 0, streamClass, null, DataInput.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadNbt(DataInput input) {
|
||||
return invokeMethod(staticMethod, null, input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.8
|
||||
*/
|
||||
private static class LoadMethodSkinUpdate extends LoadCompoundMethod {
|
||||
private Object readLimiter;
|
||||
|
||||
public LoadMethodSkinUpdate(Class<?> streamClass, Class<?> readLimiterClass) {
|
||||
setMethod(getMethod(Modifier.STATIC, 0, streamClass, null, DataInput.class, readLimiterClass));
|
||||
|
||||
// Find the unlimited read limiter
|
||||
for (Field field : readLimiterClass.getDeclaredFields()) {
|
||||
if (readLimiterClass.isAssignableFrom(field.getType())) {
|
||||
try {
|
||||
readLimiter = field.get(null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot retrieve read limiter.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object loadNbt(DataInput input) {
|
||||
return invokeMethod(staticMethod, null, input, readLimiter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,22 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.EnchantFinder;
|
||||
import net.aminecraftdev.custombosses.utils.IConverter;
|
||||
import net.aminecraftdev.custombosses.utils.StringUtils;
|
||||
import net.aminecraftdev.custombosses.utils.factory.NbtFactory;
|
||||
import net.aminecraftdev.custombosses.utils.itemstack.converters.EnchantConverter;
|
||||
import net.aminecraftdev.custombosses.utils.itemstack.holder.ItemStackHolder;
|
||||
import net.aminecraftdev.custombosses.utils.itemstack.converters.MaterialConverter;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
|
@ -21,48 +25,45 @@ import java.util.Map;
|
|||
*/
|
||||
public class ItemStackConverter implements IConverter<ItemStackHolder, ItemStack> {
|
||||
|
||||
private static final StringUtils STRING_UTILS = new StringUtils();
|
||||
private MaterialConverter materialConverter;
|
||||
private EnchantConverter enchantConverter;
|
||||
|
||||
public ItemStackConverter() {
|
||||
this.materialConverter = new MaterialConverter();
|
||||
this.enchantConverter = new EnchantConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackHolder to(ItemStack itemStack) {
|
||||
Material material = itemStack.getType();
|
||||
Short durability = itemStack.getDurability();
|
||||
String type = null, name = null, skullOwner = null, spawnerType = null;
|
||||
Short durability = itemStack.getDurability(), spawnerId = null;
|
||||
String type, name = null, skullOwner = null;
|
||||
List<String> lore = null, enchants = null;
|
||||
Boolean isGlowing = null;
|
||||
|
||||
if(durability == 0) {
|
||||
durability = null;
|
||||
}
|
||||
|
||||
type = this.materialConverter.to(material);
|
||||
|
||||
if(itemStack.hasItemMeta()) {
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
|
||||
if(itemMeta.hasDisplayName()) {
|
||||
name = STRING_UTILS.stripColor(itemMeta.getDisplayName());
|
||||
name = StringUtils.get().stripColor(itemMeta.getDisplayName());
|
||||
}
|
||||
|
||||
if(itemMeta.hasLore()) {
|
||||
lore = new ArrayList<>();
|
||||
|
||||
for(String string : itemMeta.getLore()) {
|
||||
lore.add(STRING_UTILS.stripColor(string));
|
||||
lore.add(StringUtils.get().stripColor(string));
|
||||
}
|
||||
}
|
||||
|
||||
if(itemMeta.hasEnchants()) {
|
||||
enchants = new ArrayList<>();
|
||||
|
||||
for(Map.Entry<Enchantment, Integer> entry : itemMeta.getEnchants().entrySet()) {
|
||||
int level = entry.getValue();
|
||||
Enchantment enchantment = entry.getKey();
|
||||
EnchantFinder enchantFinder = EnchantFinder.getByEnchant(enchantment);
|
||||
|
||||
if(enchantFinder == null) {
|
||||
throw new EnumConstantNotPresentException(EnchantFinder.class, "EnchantFinder couldn't find a value for " + enchantment.getName() + ". Please report this to @AMinecraftDev so he can fix it.");
|
||||
}
|
||||
|
||||
enchants.add(enchantFinder.getFancyName() + ":" + level);
|
||||
}
|
||||
enchants = this.enchantConverter.to(itemMeta.getEnchants());
|
||||
}
|
||||
|
||||
if(itemMeta instanceof SkullMeta) {
|
||||
|
@ -72,13 +73,86 @@ public class ItemStackConverter implements IConverter<ItemStackHolder, ItemStack
|
|||
skullOwner = skullMeta.getOwner();
|
||||
}
|
||||
}
|
||||
|
||||
if(itemMeta instanceof BlockStateMeta) {
|
||||
BlockStateMeta blockStateMeta = (BlockStateMeta) itemMeta;
|
||||
BlockState blockState = blockStateMeta.getBlockState();
|
||||
|
||||
if(blockState instanceof CreatureSpawner) {
|
||||
CreatureSpawner creatureSpawner = (CreatureSpawner) blockState;
|
||||
|
||||
spawnerId = creatureSpawner.getSpawnedType().getTypeId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ItemStackHolder(type, durability, name, lore, enchants, skullOwner, spawnerType, null);
|
||||
if(enchants == null) {
|
||||
ItemStack craftStack = NbtFactory.getCraftItemStack(itemStack);
|
||||
NbtFactory.NbtCompound compound = NbtFactory.fromItemTag(craftStack);
|
||||
|
||||
if(compound.containsKey("ench")) isGlowing = true;
|
||||
}
|
||||
|
||||
return new ItemStackHolder(type, durability, name, lore, enchants, skullOwner, spawnerId, isGlowing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack from(ItemStackHolder itemStackHolder) {
|
||||
return null;
|
||||
ItemStack itemStack = new ItemStack(Material.AIR);
|
||||
|
||||
if(itemStackHolder.getType() == null) return itemStack;
|
||||
|
||||
Material material = this.materialConverter.from(itemStackHolder.getType());
|
||||
|
||||
if(material == null) return itemStack;
|
||||
|
||||
itemStack.setType(material);
|
||||
|
||||
Short durability = itemStackHolder.getDurability(), spawnerId = itemStackHolder.getSpawnerId();
|
||||
String name = itemStackHolder.getName(), skullOwner = itemStackHolder.getSkullOwner();
|
||||
List<String> lore = itemStackHolder.getLore(), enchants = itemStackHolder.getEnchants();
|
||||
Boolean isGlowing = itemStackHolder.getIsGlowing();
|
||||
|
||||
if(durability != null) itemStack.setDurability(durability);
|
||||
if(enchants != null) itemStack.addEnchantments(this.enchantConverter.from(enchants));
|
||||
|
||||
if(name != null || skullOwner != null || lore != null || spawnerId != null) {
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
|
||||
if(name != null) itemMeta.setDisplayName(StringUtils.get().translateColor(name));
|
||||
if(lore != null) {
|
||||
List<String> newLore = new ArrayList<>();
|
||||
|
||||
lore.forEach(string ->newLore.add(StringUtils.get().translateColor(string)));
|
||||
itemMeta.setLore(newLore);
|
||||
}
|
||||
|
||||
if(skullOwner != null) {
|
||||
SkullMeta skullMeta = (SkullMeta) itemMeta;
|
||||
|
||||
skullMeta.setOwner(skullOwner);
|
||||
itemStack.setItemMeta(skullMeta);
|
||||
} else if(spawnerId != null) {
|
||||
BlockStateMeta blockStateMeta = (BlockStateMeta) itemMeta;
|
||||
BlockState blockState = blockStateMeta.getBlockState();
|
||||
CreatureSpawner creatureSpawner = (CreatureSpawner) blockState;
|
||||
|
||||
creatureSpawner.setSpawnedType(EntityType.fromId(spawnerId));
|
||||
blockStateMeta.setBlockState(blockState);
|
||||
itemStack.setItemMeta(blockStateMeta);
|
||||
} else {
|
||||
itemStack.setItemMeta(itemMeta);
|
||||
}
|
||||
}
|
||||
|
||||
if(isGlowing != null && isGlowing) {
|
||||
ItemStack craftStack = NbtFactory.getCraftItemStack(itemStack);
|
||||
NbtFactory.NbtCompound compound = NbtFactory.fromItemTag(craftStack);
|
||||
|
||||
compound.put("ench", NbtFactory.createList());
|
||||
return craftStack;
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.NumberUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class ItemStackUtils {
|
||||
|
||||
private static final Pattern SPLIT_PATTERN = Pattern.compile("((.*)[:+',;.](\\d+))");
|
||||
private static final NumberUtils NUMBER_UTILS = new NumberUtils();
|
||||
|
||||
public ItemStack getItemStackByString(String id) {
|
||||
int itemId = 0;
|
||||
short metaData = 0;
|
||||
String itemName;
|
||||
|
||||
Matcher matcher = SPLIT_PATTERN.matcher(id);
|
||||
|
||||
if(matcher.matches()) {
|
||||
itemName = matcher.group(2);
|
||||
metaData = Short.parseShort(matcher.group(3));
|
||||
} else {
|
||||
itemName = id;
|
||||
}
|
||||
|
||||
if(NUMBER_UTILS.isInt(itemName)) {
|
||||
itemId = Integer.parseInt(itemName);
|
||||
} else if(NUMBER_UTILS.isInt(id)) {
|
||||
itemId = Integer.parseInt(id);
|
||||
} else {
|
||||
itemName = itemName.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
Material material = Material.getMaterial(itemId);
|
||||
|
||||
if(material == null) {
|
||||
material = Material.getMaterial(itemName);
|
||||
}
|
||||
|
||||
if(material == null) material = Material.AIR;
|
||||
|
||||
return new ItemStack(material, 1, metaData);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack.converters;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.EnchantFinder;
|
||||
import net.aminecraftdev.custombosses.utils.IConverter;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class EnchantConverter implements IConverter<List<String>, Map<Enchantment, Integer>> {
|
||||
|
||||
@Override
|
||||
public List<String> to(Map<Enchantment, Integer> enchantmentIntegerMap) {
|
||||
List<String> enchants = new ArrayList<>();
|
||||
|
||||
for(Map.Entry<Enchantment, Integer> entry : enchantmentIntegerMap.entrySet()) {
|
||||
int level = entry.getValue();
|
||||
Enchantment enchantment = entry.getKey();
|
||||
EnchantFinder enchantFinder = EnchantFinder.getByEnchant(enchantment);
|
||||
|
||||
if(enchantFinder == null) {
|
||||
throw new EnumConstantNotPresentException(EnchantFinder.class, "EnchantFinder couldn't find a value for " + enchantment.getName() + ". Please report this to @AMinecraftDev so he can fix it.");
|
||||
}
|
||||
|
||||
enchants.add(enchantFinder.getFancyName() + ":" + level);
|
||||
}
|
||||
|
||||
return enchants;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Enchantment, Integer> from(List<String> strings) {
|
||||
Map<Enchantment, Integer> enchantments = new HashMap<>();
|
||||
|
||||
for(String s : strings) {
|
||||
String[] split = s.split(":");
|
||||
String fancyName = split[0];
|
||||
Integer level = Integer.parseInt(split[1]);
|
||||
EnchantFinder enchantFinder = EnchantFinder.getByName(fancyName);
|
||||
|
||||
if(enchantFinder == null) continue;
|
||||
|
||||
enchantments.put(enchantFinder.getEnchantment(), level);
|
||||
}
|
||||
|
||||
return enchantments;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack.converters;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.IConverter;
|
||||
import net.aminecraftdev.custombosses.utils.NumberUtils;
|
||||
import org.bukkit.Material;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class MaterialConverter implements IConverter<String, Material> {
|
||||
|
||||
@Override
|
||||
public String to(Material material) {
|
||||
return material.name().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material from(String input) {
|
||||
if(NumberUtils.get().isInt(input)) {
|
||||
return Material.getMaterial(Integer.parseInt(input));
|
||||
}
|
||||
|
||||
if(input.contains(":")) {
|
||||
String[] split = input.split(":");
|
||||
|
||||
input = split[0];
|
||||
|
||||
if(NumberUtils.get().isInt(input)) {
|
||||
return Material.getMaterial(Integer.parseInt(input));
|
||||
}
|
||||
}
|
||||
|
||||
return Material.matchMaterial(input);
|
||||
}
|
||||
}
|
|
@ -17,17 +17,17 @@ public class ItemStackHolder {
|
|||
@Expose private List<String> lore;
|
||||
@Expose private List<String> enchants;
|
||||
@Expose private String skullOwner;
|
||||
@Expose private String spawnerType;
|
||||
@Expose private Short spawnerId;
|
||||
@Expose private Boolean glowing;
|
||||
|
||||
public ItemStackHolder(String type, Short durability, String name, List<String> lore, List<String> enchants, String skullOwner, String spawnerType, Boolean glowing) {
|
||||
public ItemStackHolder(String type, Short durability, String name, List<String> lore, List<String> enchants, String skullOwner, Short spawnerId, Boolean glowing) {
|
||||
this.type = type;
|
||||
this.durability = durability;
|
||||
this.name = name;
|
||||
this.lore = lore;
|
||||
this.enchants = enchants;
|
||||
this.skullOwner = skullOwner;
|
||||
this.spawnerType = spawnerType;
|
||||
this.spawnerId = spawnerId;
|
||||
this.glowing = glowing;
|
||||
}
|
||||
|
||||
|
@ -55,8 +55,8 @@ public class ItemStackHolder {
|
|||
return skullOwner;
|
||||
}
|
||||
|
||||
public String getSpawnerType() {
|
||||
return spawnerType;
|
||||
public Short getSpawnerId() {
|
||||
return spawnerId;
|
||||
}
|
||||
|
||||
public Boolean getIsGlowing() {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack.repository;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class ItemData {
|
||||
|
||||
private final int itemNo;
|
||||
private final short itemData;
|
||||
|
||||
protected ItemData(final int itemNo, final short itemData) {
|
||||
this.itemNo = itemNo;
|
||||
this.itemData = itemData;
|
||||
}
|
||||
|
||||
private int getItemNo() {
|
||||
return itemNo;
|
||||
}
|
||||
|
||||
private short getItemData() {
|
||||
return itemData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (31 * itemNo) ^ itemData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(o instanceof ItemData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemData pairo = (ItemData) o;
|
||||
return this.itemNo == pairo.getItemNo() && this.itemData == pairo.getItemData();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack.repository;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.IReloadable;
|
||||
import net.aminecraftdev.custombosses.utils.NumberUtils;
|
||||
import net.aminecraftdev.custombosses.utils.file.ManagedFile;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class ItemStackRepository implements IReloadable {
|
||||
|
||||
private static ItemStackRepository instance;
|
||||
|
||||
private final Pattern csvSplitPattern = Pattern.compile("(\"([^\"]*)\"|[^,]*)(,|$)");
|
||||
private final Pattern splitPattern = Pattern.compile("((.*)[:+',;.](\\d+))");
|
||||
private final Map<String, Integer> items = new HashMap<>();
|
||||
private final Map<ItemData, List<String>> names = new HashMap<>();
|
||||
private final Map<ItemData, String> primaryName = new HashMap<>();
|
||||
private final Map<String, Short> durabilities = new HashMap<>();
|
||||
private final Map<String, String> nbtData = new HashMap<>();
|
||||
private final ManagedFile file;
|
||||
|
||||
public ItemStackRepository(JavaPlugin javaPlugin) {
|
||||
instance = this;
|
||||
|
||||
file = new ManagedFile("items.csv", javaPlugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
final List<String> lines = file.getLines();
|
||||
|
||||
if (lines.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
durabilities.clear();
|
||||
items.clear();
|
||||
names.clear();
|
||||
primaryName.clear();
|
||||
|
||||
for (String line : lines) {
|
||||
if (line.length() > 0 && line.charAt(0) == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
String itemName = null;
|
||||
int numeric = -1;
|
||||
short data = 0;
|
||||
String nbt = null;
|
||||
|
||||
int col = 0;
|
||||
Matcher matcher = csvSplitPattern.matcher(line);
|
||||
while (matcher.find()) {
|
||||
String match = matcher.group(1);
|
||||
if (StringUtils.stripToNull(match) == null) {
|
||||
continue;
|
||||
}
|
||||
match = StringUtils.strip(match.trim(), "\"");
|
||||
switch (col) {
|
||||
case 0:
|
||||
itemName = match.toLowerCase(Locale.ENGLISH);
|
||||
break;
|
||||
case 1:
|
||||
numeric = Integer.parseInt(match);
|
||||
break;
|
||||
case 2:
|
||||
data = Short.parseShort(match);
|
||||
break;
|
||||
case 3:
|
||||
nbt = StringUtils.stripToNull(match);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
col++;
|
||||
}
|
||||
// Invalid row
|
||||
if (itemName == null || numeric < 0) {
|
||||
continue;
|
||||
}
|
||||
durabilities.put(itemName, data);
|
||||
items.put(itemName, numeric);
|
||||
if (nbt != null) {
|
||||
nbtData.put(itemName, nbt);
|
||||
}
|
||||
|
||||
ItemData itemData = new ItemData(numeric, data);
|
||||
if (names.containsKey(itemData)) {
|
||||
List<String> nameList = names.get(itemData);
|
||||
nameList.add(itemName);
|
||||
} else {
|
||||
List<String> nameList = new ArrayList<>();
|
||||
nameList.add(itemName);
|
||||
names.put(itemData, nameList);
|
||||
primaryName.put(itemData, itemName);
|
||||
}
|
||||
}
|
||||
|
||||
for (List<String> nameList : names.values()) {
|
||||
nameList.sort(LengthCompare.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
public ItemStack getItemStack(String id, int quantity) {
|
||||
ItemStack itemStack = getItemStack(id.toLowerCase(Locale.ENGLISH));
|
||||
|
||||
itemStack.setAmount(quantity);
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack(String id) {
|
||||
int itemid = 0;
|
||||
String itemname;
|
||||
short metaData = 0;
|
||||
Matcher parts = splitPattern.matcher(id);
|
||||
if (parts.matches()) {
|
||||
itemname = parts.group(2);
|
||||
metaData = Short.parseShort(parts.group(3));
|
||||
} else {
|
||||
itemname = id;
|
||||
}
|
||||
|
||||
if (NumberUtils.get().isInt(itemname)) {
|
||||
itemid = Integer.parseInt(itemname);
|
||||
} else if (NumberUtils.get().isInt(id)) {
|
||||
itemid = Integer.parseInt(id);
|
||||
} else {
|
||||
itemname = itemname.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
if (itemid < 1) {
|
||||
if (items.containsKey(itemname)) {
|
||||
itemid = items.get(itemname);
|
||||
if (durabilities.containsKey(itemname) && metaData == 0) {
|
||||
metaData = durabilities.get(itemname);
|
||||
}
|
||||
} else if (Material.getMaterial(itemname.toUpperCase(Locale.ENGLISH)) != null) {
|
||||
Material bMaterial = Material.getMaterial(itemname.toUpperCase(Locale.ENGLISH));
|
||||
itemid = bMaterial.getId();
|
||||
} else {
|
||||
try {
|
||||
Material bMaterial = Bukkit.getUnsafe().getMaterialFromInternalName(itemname.toLowerCase(Locale.ENGLISH));
|
||||
itemid = bMaterial.getId();
|
||||
} catch (Throwable throwable) {
|
||||
// throw new Exception(tl("unknownItemName", itemname), throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemid < 1) {
|
||||
// throw new Exception(tl("unknownItemName", itemname));
|
||||
}
|
||||
|
||||
final Material mat = Material.getMaterial(itemid);
|
||||
if (mat == null) {
|
||||
// throw new Exception(tl("unknownItemId", itemid));
|
||||
}
|
||||
ItemStack retval = new ItemStack(mat);
|
||||
if (nbtData.containsKey(itemname)) {
|
||||
String nbt = nbtData.get(itemname);
|
||||
if (nbt.startsWith("*")) {
|
||||
nbt = nbtData.get(nbt.substring(1));
|
||||
}
|
||||
// retval = ess.getServer().getUnsafe().modifyItemStack(retval, nbt);
|
||||
}
|
||||
if (mat == Material.MOB_SPAWNER) {
|
||||
if (metaData == 0) metaData = EntityType.PIG.getTypeId();
|
||||
try {
|
||||
// retval = ess.getSpawnerProvider().setEntityType(retval, EntityType.fromId(metaData));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// throw new Exception("Can't spawn entity ID " + metaData + " from mob spawners.");
|
||||
}
|
||||
} else if (mat == Material.MONSTER_EGG) {
|
||||
EntityType type;
|
||||
try {
|
||||
type = EntityType.fromId(metaData);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// throw new Exception("Can't spawn entity ID " + metaData + " from spawn eggs.");
|
||||
}
|
||||
// retval = ess.getSpawnEggProvider().createEggItem(type);
|
||||
} else if (mat.name().endsWith("POTION")) {
|
||||
// && ReflUtil.getNmsVersionObject().isLowerThan(ReflUtil.V1_11_R1)) { // Only apply this to pre-1.11 as items.csv might only work in 1.11
|
||||
// retval = ess.getPotionMetaProvider().createPotionItem(mat, metaData);
|
||||
} else {
|
||||
retval.setDurability(metaData);
|
||||
}
|
||||
retval.setAmount(mat.getMaxStackSize());
|
||||
return retval;
|
||||
}
|
||||
|
||||
public static ItemStackRepository get() {
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package net.aminecraftdev.custombosses.utils.itemstack.repository;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class LengthCompare implements Comparator<String> {
|
||||
|
||||
protected static final LengthCompare INSTANCE = new LengthCompare();
|
||||
|
||||
private LengthCompare() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return o1.length() - o2.length();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package net.aminecraftdev.custombosses.utils;
|
||||
|
||||
import net.aminecraftdev.custombosses.utils.itemstack.ItemStackUtils;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Charles Cullen
|
||||
* @version 1.0.0
|
||||
* @since 28-Apr-18
|
||||
*/
|
||||
public class ItemStackUtilsTest {
|
||||
|
||||
private ItemStackUtils itemStackUtils;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
this.itemStackUtils = new ItemStackUtils();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItemStackId() {
|
||||
System.out.println("--------------------------------------------------------------");
|
||||
System.out.println("Testing 'getItemStackId'...");
|
||||
|
||||
ItemStack itemStack = this.itemStackUtils.getItemStackByString("diamondhelmet");
|
||||
|
||||
System.out.println("type > " + itemStack.getType());
|
||||
System.out.println("durability > " + itemStack.getDurability());
|
||||
System.out.println("--------------------------------------------------------------");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
"SkeletonKing": {
|
||||
"editing": false,
|
||||
"mainStats": {
|
||||
"entityType": "SKELETON",
|
||||
"health": 500,
|
||||
"displayName": "&6&lSkeleton King Boss"
|
||||
},
|
||||
"spawnItem": "SKSpawnEgg",
|
||||
"equipment": {
|
||||
"helmet": "SKHelmet",
|
||||
"chestplate": "SKChestplate",
|
||||
"leggings": "SKLeggings",
|
||||
"boots": "SKBoots"
|
||||
},
|
||||
"hands": {
|
||||
"mainHand": "SKMainHand",
|
||||
"offHand": "SKOffHand"
|
||||
},
|
||||
"potions": [
|
||||
{
|
||||
"type": "resistance",
|
||||
"level": 3,
|
||||
"duration": -1
|
||||
},
|
||||
{
|
||||
"type": "speed",
|
||||
"level": 1,
|
||||
"duration": 500
|
||||
}
|
||||
],
|
||||
"skills": {
|
||||
"overallChance": 35.5,
|
||||
"masterMessage": "SKMainSkillMessage",
|
||||
"skills": [
|
||||
"KNOCKBACK1",
|
||||
"CAGE1"
|
||||
]
|
||||
},
|
||||
"drops": {
|
||||
"naturalDrops": false,
|
||||
"dropExp": false,
|
||||
"dropTable": "SKTable"
|
||||
},
|
||||
"messages": {
|
||||
"onSpawn": "SKOnSpawn",
|
||||
"onDeath": "SKOnDeath",
|
||||
"taunts": {
|
||||
"delay": 60,
|
||||
"taunts": [
|
||||
"SKTaunt1",
|
||||
"SKTaunt2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"onSpawn": "SKOnSpawn",
|
||||
"onDeath": "SKOnDeath"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
Hooks:
|
||||
ASkyBlock: false
|
||||
Factions: false
|
||||
StackMob: false
|
||||
WorldEdit: true
|
||||
WorldGuard: true
|
||||
Menu:
|
||||
enabledBlock: EMERALD_BLOCK
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
|
||||
}
|
677
jars/editor.yml
677
jars/editor.yml
|
@ -1,677 +0,0 @@
|
|||
# MainPanel: # panel name #
|
||||
# name: '&b&l{boss} Editor' # panel display name #
|
||||
# slots: 45 # panel size #
|
||||
# Settings: # settings section #
|
||||
# emptySpaceFiller: true # fill in empty space #
|
||||
# fillTo: 0 # fill to slot #
|
||||
# backButton: false # use back button #
|
||||
# exitButton: false # use exit button #
|
||||
# EmptySpaceFiller: # empty space filler itemstack #
|
||||
# type: '160:0' # empty space filler type #
|
||||
# name: '&7' # empty space filler name #
|
||||
# Buttons: # buttons section #
|
||||
# BackButton: 9 # back button slot #
|
||||
# ExitButton: 9 # exit button slot #
|
||||
|
||||
MainPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 45
|
||||
Settings:
|
||||
emptySpaceFiller: true
|
||||
EmptySpaceFiller:
|
||||
type: '160:0'
|
||||
name: '&7'
|
||||
Items:
|
||||
'12':
|
||||
type: DIAMOND
|
||||
name: '&c&lDrops Manager'
|
||||
lore:
|
||||
- '&7Click here to manage the drop table'
|
||||
- '&7that is attached to this boss.'
|
||||
Button: Drops
|
||||
'14':
|
||||
type: DIAMOND_HELMET
|
||||
name: '&c&lEquipment Manager'
|
||||
lore:
|
||||
- '&7Click here to manage the equipment'
|
||||
- '&7that the boss has equipped.'
|
||||
Button: Equipment
|
||||
'16':
|
||||
type: BONE
|
||||
name: '&a&lTargeting Manager'
|
||||
lore:
|
||||
- '&7Click here to edit how the boss handles'
|
||||
- '&7targeting of players and mobs.'
|
||||
Button: Targeting
|
||||
'22':
|
||||
type: BOW
|
||||
name: '&c&lWeapon Manager'
|
||||
lore:
|
||||
- '&7Click here to manage the weapon(s)'
|
||||
- '&7that the boss has equipped.'
|
||||
Button: Weapon
|
||||
'23':
|
||||
type: BARRIER
|
||||
name: '&c&l!&4&l!&c&l! &4&lWARNING &c&l!&4&l!&c&l!'
|
||||
lore:
|
||||
- '&7While editing is enabled for this boss'
|
||||
- '&7no one will be able to spawn it, nor'
|
||||
- '&7will it spawn naturally.'
|
||||
'24':
|
||||
type: BLAZE_POWDER
|
||||
name: '&c&lSkill Manager'
|
||||
lore:
|
||||
- '&7Click here to manage the assigned'
|
||||
- '&7skill(s) the boss has and their occurrence'
|
||||
- '&7chances.'
|
||||
Button: Skill
|
||||
'32':
|
||||
type: '351:4'
|
||||
name: '&a&lStatistics Manager'
|
||||
lore:
|
||||
- '&7Click here to edit the statistics of the'
|
||||
- '&7boss, including things like: health,'
|
||||
- '&7potion effects, commands on spawn, etc.'
|
||||
Button: Stats
|
||||
'39':
|
||||
type: REDSTONE
|
||||
name: '&a&lParticle Manager'
|
||||
lore:
|
||||
- '&7Click here to manage the particles the'
|
||||
- '&7boss has equipped during certain events.'
|
||||
Button: Particle
|
||||
'41':
|
||||
type: GRASS
|
||||
name: '&a&lSpawning Manager'
|
||||
lore:
|
||||
- '&7Click here to edit how the boss handles'
|
||||
- '&7spawning.'
|
||||
Button: Spawning
|
||||
'43':
|
||||
type: BOOK
|
||||
name: '&a&lText Manager'
|
||||
lore:
|
||||
- '&7Click here to edit the taunts, sayings,'
|
||||
- '&7etc. for this boss.'
|
||||
Button: Text
|
||||
DropsPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&b&lSelected Drop Table'
|
||||
lore:
|
||||
- '&7The current selected drop'
|
||||
- '&7table is: &b{dropTable}&7.'
|
||||
- '&7'
|
||||
- '&b&lHints'
|
||||
- '&b&l* &7If this shows N/A it means'
|
||||
- '&7 there was an issue loading the'
|
||||
- '&7 previous table, or it doesn''t'
|
||||
- '&7 have one selected.'
|
||||
- '&b&l* &7Click here to go straight to the'
|
||||
- '&7 editing screen of the drop table.'
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of drop tables.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lCreate a new Drop Table'
|
||||
lore:
|
||||
- '&7Click here to create a new drop'
|
||||
- '&7table. It will automatically be'
|
||||
- '&7assigned to this boss when created.'
|
||||
Button: CreateDropTable
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of drop tables.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: BOOK
|
||||
name: '&c&lDrops Guide'
|
||||
lore:
|
||||
- '&7When selecting the drop table for this custom boss'
|
||||
- '&7you can either choose from one of the above listed'
|
||||
- '&7pre-configured drop tables or you can make a'
|
||||
- '&7new one for this boss.'
|
||||
- '&7'
|
||||
- '&c&lHints'
|
||||
- '&c&l* &7The currently selected drop table will be shown'
|
||||
- '&7 with an emerald which states so.'
|
||||
- '&c&l* &7Every d rop table from every boss will be listed'
|
||||
- '&7 here as an available drop table.'
|
||||
EquipmentPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 9
|
||||
Settings:
|
||||
backButton: true
|
||||
emptySpaceFiller: true
|
||||
EmptySpaceFiller:
|
||||
type: '160:0'
|
||||
name: '&7'
|
||||
Buttons:
|
||||
BackButton: 9
|
||||
Items:
|
||||
'2':
|
||||
type: DIAMOND_HELMET
|
||||
name: '&c&lHelmet'
|
||||
lore:
|
||||
- '&7Click here to change the'
|
||||
- '&7helmet for the &f{boss}'
|
||||
- '&7or add one from your'
|
||||
- '&7inventory.'
|
||||
Button: Helmet
|
||||
'3':
|
||||
type: DIAMOND_CHESTPLATE
|
||||
name: '&c&lChestplate'
|
||||
lore:
|
||||
- '&7Click here to change the'
|
||||
- '&7chestplate for the &f{boss}'
|
||||
- '&7or add one from your'
|
||||
- '&7inventory.'
|
||||
Button: Chestplate
|
||||
'4':
|
||||
type: DIAMOND_LEGGINGS
|
||||
name: '&c&lLeggings'
|
||||
lore:
|
||||
- '&7Click here to change the'
|
||||
- '&7leggings for the &f{boss}'
|
||||
- '&7or add one from your'
|
||||
- '&7inventory.'
|
||||
Button: Leggings
|
||||
'5':
|
||||
type: DIAMOND_BOOTS
|
||||
name: '&c&lBoots'
|
||||
lore:
|
||||
- '&7Click here to change the'
|
||||
- '&7boots for the &f{boss}'
|
||||
- '&7or add one from your'
|
||||
- '&7inventory.'
|
||||
Button: Boots
|
||||
'8':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
'9':
|
||||
type: BOOK
|
||||
name: '&c&lEquipment Guide'
|
||||
lore:
|
||||
- '&7here you can choose what equipment'
|
||||
- '&7this boss has. To choose simply click'
|
||||
- '&7the desired piece, then click one of'
|
||||
- '&7the preset pieces or click the diamond'
|
||||
- '&7block to add a new piece from your'
|
||||
- '&7inventory.'
|
||||
HelmetPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped helmet.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of helmets.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Helmet'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7helmet which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of helmets.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
ChestplatePanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped chestplate.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of chestplates.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Chestplate'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7chestplate which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of chestplates.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
LeggingsPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped {type}.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of leggings.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Leggings'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7leggings which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of leggings.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
BootsPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped {type}.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of boots.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Boots'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7boots which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of boots.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
TargetingPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 27
|
||||
Settings:
|
||||
backButton: true
|
||||
emptySpaceFiller: true
|
||||
EmptySpaceFiller:
|
||||
type: '160:0'
|
||||
name: '&7'
|
||||
Buttons:
|
||||
BackButton: 27
|
||||
Items:
|
||||
'5':
|
||||
type: REDSTONE_BLOCK
|
||||
name: '&e&lSpawner'
|
||||
lore:
|
||||
- '&7This target system will only'
|
||||
- '&7target the player who spawned'
|
||||
- '&7the boss.'
|
||||
TargetingSystem: Spawner
|
||||
'12':
|
||||
type: REDSTONE_BLOCK
|
||||
name: '&e&lLast Damage'
|
||||
lore:
|
||||
- '&7This target system will'
|
||||
- '&7target the last player that'
|
||||
- '&7damaged the boss and update'
|
||||
- '&7every time someone hits the'
|
||||
- '&7boss.'
|
||||
TargetingSystem: LastDamage
|
||||
'16':
|
||||
type: REDSTONE_BLOCK
|
||||
name: '&e&lTop Damage'
|
||||
lore:
|
||||
- '&7This target system will'
|
||||
- '&7target the player who has'
|
||||
- '&7the most damage that is'
|
||||
- '&7nearby.'
|
||||
TargetingSystem: TopDamage
|
||||
'23':
|
||||
type: BOOK
|
||||
name: '&c&lTargeting Guide'
|
||||
lore:
|
||||
- '&7Here you can choose how'
|
||||
- '&7the boss handles targeting'
|
||||
- '&7of players.'
|
||||
'27':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
WeaponPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 9
|
||||
Settings:
|
||||
backButton: true
|
||||
emptySpaceFiller: true
|
||||
EmptySpaceFiller:
|
||||
type: '160:0'
|
||||
name: '&7'
|
||||
Buttons:
|
||||
BackButton: 9
|
||||
Items:
|
||||
'1':
|
||||
type: BOOK
|
||||
name: '&c&lWeapons Guide'
|
||||
lore:
|
||||
- '&7here you can choose what weapons'
|
||||
- '&7this boss has. To choose simply click'
|
||||
- '&7the desired hand, then click one of'
|
||||
- '&7the preset weapons or click the diamond'
|
||||
- '&7block to add a new weapon from your'
|
||||
- '&7inventory.'
|
||||
'4':
|
||||
type: DIAMOND_SWORD
|
||||
name: '&c&lMain Hand'
|
||||
lore:
|
||||
- '&7Click here to modify the'
|
||||
- '&7main hand for the &f{boss}&7.'
|
||||
Button: MainHand
|
||||
'6':
|
||||
type: SHIELD
|
||||
name: '&c&lOff Hand'
|
||||
lore:
|
||||
- '&7Click here to modify the'
|
||||
- '&7off hand for the &f{boss}&7.'
|
||||
Button: OffHand
|
||||
'9':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
MainHandPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped main hand.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of weapons.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Weapon'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7weapon which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of weapons.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
||||
OffHandPanel:
|
||||
name: '&b&l{boss} Editor'
|
||||
slots: 54
|
||||
Settings:
|
||||
fillTo: 45
|
||||
backButton: true
|
||||
Buttons:
|
||||
BackButton: 54
|
||||
Items:
|
||||
'46':
|
||||
type: DIAMOND
|
||||
name: '&c&lRemove'
|
||||
lore:
|
||||
- '&7click here to remove the'
|
||||
- '&7currently equipped off hand.'
|
||||
Button: Remove
|
||||
'47':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'48':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'49':
|
||||
type: ARROW
|
||||
name: '&e&l&m<-&e&l Previous Page'
|
||||
lore:
|
||||
- '&7Click here to go to the previous'
|
||||
- '&7page of weapons.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
PreviousPage: true
|
||||
'50':
|
||||
type: DIAMOND_BLOCK
|
||||
name: '&a&lAdd New Weapon'
|
||||
lore:
|
||||
- '&7Click here to add a new'
|
||||
- '&7weapon which you have in your'
|
||||
- '&7inventory.'
|
||||
Button: AddNew
|
||||
'51':
|
||||
type: ARROW
|
||||
name: '&e&lNext Page &e&l&m->'
|
||||
lore:
|
||||
- '&7Click here to go to the next'
|
||||
- '&7page of weapons.'
|
||||
- '&7'
|
||||
- '&7Currently viewing page &e{currentPage}/{maxPages}&7.'
|
||||
NextPage: true
|
||||
'52':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'53':
|
||||
type: STAINED_GLASS_PANE
|
||||
name: '&7'
|
||||
'54':
|
||||
type: PAPER
|
||||
name: '&e&lGo Back'
|
||||
lore:
|
||||
- '&7Click here to go back.'
|
|
@ -1,57 +0,0 @@
|
|||
{
|
||||
"SKSpawnItem": {
|
||||
"type": "383",
|
||||
"durability": "51",
|
||||
"name": "&6&lSkeleton King Boss Spawn Egg",
|
||||
"lore": [
|
||||
"&7Right click a block to spawn",
|
||||
"&7the boss as that location."
|
||||
],
|
||||
"skullOwner": "",
|
||||
"enchants": [],
|
||||
"spawnerType": ""
|
||||
},
|
||||
"SKHelmet": {
|
||||
"type": "gold_helmet",
|
||||
"durability": "",
|
||||
"name": "",
|
||||
"lore": [],
|
||||
"owner": "",
|
||||
"enchants": [ "protection:4", "unbreaking:3" ],
|
||||
"spawnerType": ""
|
||||
},
|
||||
"SKChestplate": {
|
||||
"type": "gold_chestplate",
|
||||
"durability": "",
|
||||
"name": "",
|
||||
"lore": [],
|
||||
"owner": "",
|
||||
"enchants": [ "protection:4", "unbreaking:3" ],
|
||||
"spawnerType": ""
|
||||
},
|
||||
"SKLeggings": {
|
||||
"type": "gold_leggings",
|
||||
"durability": "",
|
||||
"name": "",
|
||||
"lore": [],
|
||||
"owner": "",
|
||||
"enchants": [ "protection:4", "unbreaking:3" ],
|
||||
"spawnerType": ""
|
||||
},
|
||||
"SKBoots": {
|
||||
"type": "diamond_boots",
|
||||
"durability": "",
|
||||
"name": "",
|
||||
"lore": [],
|
||||
"owner": "",
|
||||
"enchants": [ "protection:4", "unbreaking:3" ],
|
||||
"spawnerType": ""
|
||||
},
|
||||
"SKMainHand": {
|
||||
"type": "diamond_sword",
|
||||
"enchants": [ "sharpness:4", "unbreaking:3" ]
|
||||
},
|
||||
"SKOffHand": {
|
||||
"type": "shield"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
|
||||
}
|
|
@ -9,4 +9,10 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
*/
|
||||
public class CustomBosses extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
41
pom.xml
41
pom.xml
|
@ -20,7 +20,7 @@
|
|||
</modules>
|
||||
|
||||
<properties>
|
||||
<plugin.version>3.0.0-SNAPSHOT-U6</plugin.version>
|
||||
<plugin.version>3.0.0-SNAPSHOT-U7</plugin.version>
|
||||
<plugin.name>CustomBosses</plugin.name>
|
||||
<plugin.main>net.aminecraftdev.custombosses.CustomBosses</plugin.main>
|
||||
<plugin.author>AMinecraftDev</plugin.author>
|
||||
|
@ -138,45 +138,6 @@
|
|||
</configuration>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/jars</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>plugin-modules/Core/resources-json</directory>
|
||||
<includes>
|
||||
<include>*.json</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>plugin.yml</exclude>
|
||||
</excludes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>plugin-modules/Core/resources-yml</directory>
|
||||
<includes>
|
||||
<include>*.yml</include>
|
||||
<include>items.csv</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>plugin.yml</exclude>
|
||||
</excludes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
|
|
Loading…
Reference in New Issue