-
-
Save Sxtanna/131df31228e497e7cb17d96bb41019cc to your computer and use it in GitHub Desktop.
Small util to load and save structures, using nms and reflection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.bukkit.Bukkit; | |
import org.bukkit.Location; | |
import org.bukkit.util.Vector; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
/** | |
* "Small" util for loading and saving structures | |
* | |
* <br>Currently tested with 1.10 and 1.11</br> | |
*/ | |
@SuppressWarnings({"FieldCanBeLocal", "unchecked"}) | |
public class StructureUtil { | |
private static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; | |
private static final String SAVE_NAME = VERSION.startsWith("v1_11") ? "c" : "d"; | |
private static Class block; | |
private static Class world; | |
private static Class blocks; | |
private static Class blockPos; | |
private static Class worldServer; | |
private static Class craftWorld; | |
private static Class minecraftKey; | |
private static Class minecraftServer; | |
private static Class enumBlockMirror; | |
private static Class enumBlockRotation; | |
private static Class definedStructure; | |
private static Class definedStructureInfo; | |
private static Class definedStructureManager; | |
private static Object structureVoid; | |
private static Method getStructure; | |
private static Method getStructureManager; | |
private static Method getHandle; | |
private static Method getMinecraftServer; | |
private static Method enumBlockMirrorValueOf; | |
private static Method enumBlockRotationValueOf; | |
private static Method setPos; | |
private static Method setAuthor; | |
private static Method save; | |
private static Method load; | |
private static Method loadInfo; | |
private static Method mirror; | |
private static Method rotation; | |
private static Method ignoreEntities; | |
private static Constructor blockPosConstruct; | |
private static Constructor minecraftKeyConstruct; | |
private static Constructor definedStructureInfoConstruct; | |
static { | |
try { | |
block = nmsClass("Block"); | |
world = nmsClass("World"); | |
blocks = nmsClass("Blocks"); | |
blockPos = nmsClass("BlockPosition"); | |
craftWorld = obcClass("CraftWorld"); | |
worldServer = nmsClass("WorldServer"); | |
minecraftKey = nmsClass("MinecraftKey"); | |
minecraftServer = nmsClass("MinecraftServer"); | |
enumBlockMirror = nmsClass("EnumBlockMirror"); | |
enumBlockRotation = nmsClass("EnumBlockRotation"); | |
definedStructure = nmsClass("DefinedStructure"); | |
definedStructureInfo = nmsClass("DefinedStructureInfo"); | |
definedStructureManager = nmsClass("DefinedStructureManager"); | |
structureVoid = blocks.getField("gj").get(null); | |
save = forArgsOf(definedStructureManager, SAVE_NAME, minecraftServer, minecraftKey); | |
load = forArgsOf(definedStructure, "a", world, blockPos, definedStructureInfo); | |
loadInfo = forArgsOf(definedStructureManager, "b", minecraftServer, minecraftKey); | |
mirror = forArgsOf(definedStructureInfo, "a", enumBlockMirror); | |
rotation = forArgsOf(definedStructureInfo, "a", enumBlockRotation); | |
ignoreEntities = forArgsOf(definedStructureInfo, "a", boolean.class); | |
setPos = forArgsOf(definedStructure, "a", world, blockPos, blockPos, boolean.class, block); | |
setAuthor = forArgsOf(definedStructure, "a", String.class); | |
getHandle = forArgsOf(craftWorld, "getHandle"); | |
getStructure = forArgsOf(definedStructureManager, "a", minecraftServer, minecraftKey); | |
getMinecraftServer = forArgsOf(worldServer, "getMinecraftServer"); | |
getStructureManager = forArgsOf(worldServer, "y"); | |
enumBlockMirrorValueOf = forArgsOf(enumBlockMirror, "valueOf", String.class); | |
enumBlockRotationValueOf = forArgsOf(enumBlockRotation, "valueOf", String.class); | |
blockPosConstruct = forArgs(blockPos, int.class, int.class, int.class); | |
minecraftKeyConstruct = forArgs(minecraftKey, String.class); | |
definedStructureInfoConstruct = forArgs(definedStructureInfo); | |
} | |
catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) { | |
System.out.println("Failed to initialize reflection fields"); | |
e.printStackTrace(); | |
} | |
} | |
/** | |
* Save a Structure | |
* | |
* @param start The starting Location | |
* @param offSet The Offset, represented as a Vector | |
* @param name The name of the Structure | |
* @param author The name of the Author | |
* @param includeEntities If entities should be included in the save | |
* | |
* @return True if saved Successfully | |
*/ | |
public static boolean save(Location start, Vector offSet, String name, String author, boolean includeEntities) { | |
try { | |
Object startPos = newInstance(blockPosConstruct, start.getBlockX(), start.getBlockY(), start.getBlockZ()); | |
Object sizePos = newInstance(blockPosConstruct, offSet.getBlockX(), offSet.getBlockY(), offSet.getBlockZ()); | |
Object world = getHandle.invoke(craftWorld.cast(start.getWorld())); | |
Object server = getMinecraftServer.invoke(world); | |
Object minecraftKey = newInstance(minecraftKeyConstruct, name); | |
Object structureManager = getStructureManager.invoke(world); | |
Object structure = getStructure.invoke(structureManager, server, minecraftKey); | |
setPos.invoke(structure, world, startPos, sizePos, includeEntities, structureVoid); | |
setAuthor.invoke(structure, author); | |
return ((boolean) save.invoke(structureManager, server, minecraftKey)); | |
} | |
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
System.out.println("Failed to save Structure " + name + " by " + author); | |
e.printStackTrace(); | |
} | |
return false; | |
} | |
/** | |
* Load a Structure | |
* | |
* @param where Where to load this Structure | |
* @param name The name of the Structure | |
* @param mirrorType How it should be Mirrored [FRONT_BACK, LEFT_RIGHT or NONE] | |
* @param rotateType How it should be rotated [CLOCKWISE_90, CLOCKWISE_180, COUNTERCLOCKWISE_90 or NONE] | |
* @param includeEntities If saved entities should be loaded | |
* | |
* @return True if it loaded Successfully | |
*/ | |
public static boolean load(Location where, String name, String mirrorType, String rotateType, boolean includeEntities) { | |
try { | |
Object wherePos = newInstance(blockPosConstruct, where.getBlockX(), where.getBlockY(), where.getBlockZ()); | |
Object world = getHandle.invoke(craftWorld.cast(where.getWorld())); | |
Object server = getMinecraftServer.invoke(world); | |
Object minecraftKey = newInstance(minecraftKeyConstruct, name); | |
Object structureManager = getStructureManager.invoke(world); | |
Object structure = loadInfo.invoke(structureManager, server, minecraftKey); | |
if (structure != null) { | |
Object info = newInstance(definedStructureInfoConstruct); | |
mirror.invoke(info, enumBlockMirrorValueOf.invoke(null, mirrorType)); | |
rotation.invoke(info, enumBlockRotationValueOf.invoke(null, rotateType)); | |
ignoreEntities.invoke(info, includeEntities); | |
load.invoke(structure, world, wherePos, info); | |
return true; | |
} | |
} | |
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
System.out.println("Failed to load Structure " + name + " at " + where.toVector() + " with params [" + mirrorType + ", " + rotateType + ", " + includeEntities + "]"); | |
e.printStackTrace(); | |
} | |
return false; | |
} | |
/** | |
* Get a class by name from the net.minecraft.server Package | |
* | |
* @param name The name of the Class | |
* | |
* @return The Class | |
* @throws ClassNotFoundException If the Class wasn't found | |
*/ | |
private static Class<?> nmsClass(String name) throws ClassNotFoundException { | |
return Class.forName("net.minecraft.server." + VERSION + "." + name); | |
} | |
/** | |
* Get a class by name from the org.bukkit.craftbukkit Package | |
* | |
* @param name The name of the Class | |
* | |
* @return The Class | |
* @throws ClassNotFoundException If the Class wasn't found | |
*/ | |
private static Class<?> obcClass(String name) throws ClassNotFoundException { | |
return Class.forName("org.bukkit.craftbukkit." + VERSION + "." + name); | |
} | |
/** | |
* Convenience method for getting a Constructor of a Class | |
* | |
* @param clazz The Class | |
* @param parameterTypes The target constructor's parameters | |
* | |
* @return The target Constructor | |
* @throws NoSuchMethodException If the Constructor isn't found | |
*/ | |
private static Constructor<?> forArgs(Class<?> clazz, Class<?>... parameterTypes) throws NoSuchMethodException { | |
return clazz.getConstructor(parameterTypes); | |
} | |
/** | |
* Convenience method for getting a Method of a Class | |
* | |
* @param clazz The Class | |
* @param name The name of the Method | |
* @param parameterTypes The target method's parameters | |
* | |
* @return The target Method | |
* @throws NoSuchMethodException If the Method isn't found | |
*/ | |
private static Method forArgsOf(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException { | |
return clazz.getMethod(name, parameterTypes); | |
} | |
/** | |
* Convenience method for getting an Instance of a Class | |
* | |
* @param constructor The Constructor to use | |
* @param initArgs The Constructor parameters | |
* | |
* @return The new Object instance | |
*/ | |
private static Object newInstance(Constructor<?> constructor, Object... initArgs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { | |
return constructor.newInstance(initArgs); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment