Skip to content

Instantly share code, notes, and snippets.

@theoparis
Last active August 14, 2019 23:29
Show Gist options
  • Save theoparis/f7bb9e43e0737c935842a7ed87e8013d to your computer and use it in GitHub Desktop.
Save theoparis/f7bb9e43e0737c935842a7ed87e8013d to your computer and use it in GitHub Desktop.
Spigot Minecraft Anvil GUI Helper Classes (Currently only works in Spigot/Minecraft 1.14.4)
package me.creepinson.plugin.util.gui;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import me.creepinson.plugin.util.NMSManager;
/**
* Created by chasechocolate.
*/
public class AnvilGUI {
private Player player;
private AnvilClickEventHandler handler;
private static Class<?> BlockPosition;
private static Class<?> PacketPlayOutOpenWindow;
private static Class<?> ContainerAnvil;
private static Class<?> ContainerAccess;
private static Class<?> ChatMessage;
private static Class<?> EntityHuman;
private HashMap<AnvilSlot, ItemStack> items = new HashMap<AnvilSlot, ItemStack>();
private Inventory inv;
private Listener listener;
private void loadClasses() {
BlockPosition = NMSManager.get().getNMSClass("BlockPosition");
PacketPlayOutOpenWindow = NMSManager.get().getNMSClass("PacketPlayOutOpenWindow");
ContainerAnvil = NMSManager.get().getNMSClass("ContainerAnvil");
EntityHuman = NMSManager.get().getNMSClass("EntityHuman");
ChatMessage = NMSManager.get().getNMSClass("ChatMessage");
ContainerAccess = NMSManager.get().getNMSClass("ContainerAccess");
}
public AnvilGUI(JavaPlugin plugin, final Player player, final AnvilClickEventHandler handler) {
loadClasses();
this.player = player;
this.handler = handler;
this.listener = new Listener() {
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (event.getWhoClicked() instanceof Player) {
if (event.getInventory().equals(inv)) {
ItemStack item = event.getCurrentItem();
int slot = event.getRawSlot();
String name = "";
if (item != null) {
if (item.hasItemMeta()) {
ItemMeta meta = item.getItemMeta();
if (meta.hasDisplayName()) {
name = meta.getDisplayName();
}
}
}
AnvilClickEvent clickEvent = new AnvilClickEvent(AnvilSlot.bySlot(slot), name,
getItemNameText());
handler.onAnvilClick(clickEvent);
if(clickEvent.isCanceled) {
event.setCancelled(true);
}
if (clickEvent.getWillClose()) {
event.getWhoClicked().closeInventory();
}
if (clickEvent.getWillDestroy()) {
destroy();
}
}
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
if (event.getPlayer() instanceof Player) {
Inventory inv = event.getInventory();
if (inv.equals(AnvilGUI.this.inv)) {
inv.clear();
destroy();
}
}
}
};
Bukkit.getPluginManager().registerEvents(listener, plugin);
}
public Player getPlayer() {
return player;
}
public void setSlot(AnvilSlot slot, ItemStack item) {
items.put(slot, item);
}
public void open() throws IllegalAccessException, InvocationTargetException, InstantiationException {
try {
Object p = NMSManager.get().getHandle(player);
Object container = null;
Method containerAccessAt = null;
switch (NMSManager.get().getVersion()) {
case "v1_14_R1":
containerAccessAt = NMSManager.get().getMethod(ContainerAccess, "at",
NMSManager.get().getNMSClass("World"), BlockPosition);
container = ContainerAnvil
.getConstructor(int.class, NMSManager.get().getNMSClass("PlayerInventory"), ContainerAccess)
.newInstance(1, NMSManager.get().getPlayerField(player, "inventory"),
containerAccessAt.invoke(null, NMSManager.get().getPlayerField(player, "world"),
BlockPosition.getConstructor(int.class, int.class, int.class).newInstance(0, 0,
0)));
default:
containerAccessAt = NMSManager.get().getMethod(ContainerAccess, "at",
NMSManager.get().getNMSClass("World"), BlockPosition);
container = ContainerAnvil
.getConstructor(int.class, NMSManager.get().getNMSClass("PlayerInventory"), ContainerAccess)
.newInstance(1, NMSManager.get().getPlayerField(player, "inventory"),
containerAccessAt.invoke(null, NMSManager.get().getPlayerField(player, "world"),
BlockPosition.getConstructor(int.class, int.class, int.class).newInstance(0, 0,
0)));
}
NMSManager.get().getField(NMSManager.get().getNMSClass("Container"), "checkReachable").set(container,
false);
// Set the items to the items from the inventory given
Object bukkitView = NMSManager.get().invokeMethod("getBukkitView", container);
inv = (Inventory) NMSManager.get().invokeMethod("getTopInventory", bukkitView);
for (AnvilSlot slot : items.keySet()) {
inv.setItem(slot.getSlot(), items.get(slot));
}
// Counter stuff that the game uses to keep track of inventories
int c = (int) NMSManager.get().invokeMethod("nextContainerCounter", p);
// Send the packet
Object playerConnection = null;
Object packet = null;
switch (NMSManager.get().getVersion()) {
case "v1_14_R1":
Constructor<?> chatMessageConstructor = ChatMessage.getConstructor(String.class, Object[].class);
playerConnection = NMSManager.get().getPlayerField(player, "playerConnection");
packet = PacketPlayOutOpenWindow.getConstructor(int.class, NMSManager.get().getNMSClass("Containers"),
NMSManager.get().getNMSClass("IChatBaseComponent")).newInstance(c, NMSManager.get().getNMSClass("Containers").getField("ANVIL").get(null),
chatMessageConstructor.newInstance("Repairing", new Object[] {}));
default:
chatMessageConstructor = ChatMessage.getConstructor(String.class, Object[].class);
playerConnection = NMSManager.get().getPlayerField(player, "playerConnection");
packet = PacketPlayOutOpenWindow.getConstructor(int.class, NMSManager.get().getNMSClass("Containers"),
NMSManager.get().getNMSClass("IChatBaseComponent")).newInstance(c, NMSManager.get().getNMSClass("Containers").getField("ANVIL").get(null),
chatMessageConstructor.newInstance("Repairing", new Object[] {}));
}
Method sendPacket = NMSManager.get().getMethod("sendPacket", playerConnection.getClass(),
PacketPlayOutOpenWindow);
sendPacket.invoke(playerConnection, packet);
// Set their active container to the container
Field activeContainerField = NMSManager.get().getField(EntityHuman, "activeContainer");
if (activeContainerField != null) {
activeContainerField.set(p, container);
// Set their active container window id to that counter stuff
NMSManager.get().getField(NMSManager.get().getNMSClass("Container"), "windowId")
.set(activeContainerField.get(p), c);
// Add the slot listener
NMSManager.get().getMethod("addSlotListener", activeContainerField.get(p).getClass(), p.getClass())
.invoke(activeContainerField.get(p), p);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void destroy() {
player = null;
handler = null;
items = null;
HandlerList.unregisterAll(listener);
listener = null;
}
public String getItemNameText() {
// field: repairedItemName
Object p = NMSManager.get().getHandle(player);
Field activeContainerField = NMSManager.get().getField(EntityHuman, "activeContainer");
if (activeContainerField != null) {
try {
Field renameText = NMSManager.get().getField(ContainerAnvil, "renameText");
if (renameText != null) {
return (String) renameText.get(activeContainerField.get(p));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
public enum AnvilSlot {
INPUT_LEFT(0), INPUT_RIGHT(1), OUTPUT(2);
private int slot;
private AnvilSlot(int slot) {
this.slot = slot;
}
public static AnvilSlot bySlot(int slot) {
for (AnvilSlot anvilSlot : values()) {
if (anvilSlot.getSlot() == slot) {
return anvilSlot;
}
}
return null;
}
public int getSlot() {
return slot;
}
}
public interface AnvilClickEventHandler {
void onAnvilClick(AnvilClickEvent event);
}
public class AnvilClickEvent {
private AnvilSlot slot;
private String name;
private String renameText;
private boolean close = false;
private boolean destroy = false;
public boolean isCanceled;
public AnvilClickEvent(AnvilSlot slot, String name, String renameText) {
this.slot = slot;
this.name = name;
this.renameText = renameText;
}
public String getRenameText() {
return renameText;
}
public AnvilSlot getSlot() {
return slot;
}
public String getName() {
return name;
}
public boolean getWillClose() {
return close;
}
public void setWillClose(boolean close) {
this.close = close;
}
public boolean getWillDestroy() {
return destroy;
}
public void setWillDestroy(boolean destroy) {
this.destroy = destroy;
}
}
}
package me.creepinson.command;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.creepinson.core.HelpPlugin;
import me.creepinson.plugin.util.gui.AnvilGUI;
import me.creepinson.plugin.util.gui.AnvilGUI.AnvilClickEvent;
import me.creepinson.plugin.util.gui.AnvilGUI.AnvilClickEventHandler;
import me.creepinson.plugin.util.gui.AnvilGUI.AnvilSlot;
public class Anvil implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (sender instanceof Player) {
Player p = (Player) sender;
AnvilGUI gui = new AnvilGUI(HelpPlugin.plugin, p, new AnvilClickEventHandler() {
@Override
public void onAnvilClick(AnvilClickEvent event) {
if (event.getSlot() != AnvilSlot.OUTPUT) {
p.sendMessage("Rename Text: " + event.getRenameText());
}
}
});
try {
gui.open();
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
return true;
}
}
package me.creepinson.plugin.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class NMSManager {
private static NMSManager instance;
public static NMSManager get(){
if(instance == null)
instance = new NMSManager();
return instance;
}
public static final Map<Class<?>, Class<?>> CORRESPONDING_TYPES = new HashMap<Class<?>, Class<?>>();
public Class<?> getPrimitiveType(Class<?> clazz) {
return CORRESPONDING_TYPES.containsKey(clazz) ? CORRESPONDING_TYPES
.get(clazz) : clazz;
}
public Class<?>[] toPrimitiveTypeArray(Class<?>[] classes) {
int a = classes != null ? classes.length : 0;
Class<?>[] types = new Class<?>[a];
for (int i = 0; i < a; i++)
types[i] = getPrimitiveType(classes[i]);
return types;
}
public static boolean equalsTypeArray(Class<?>[] a, Class<?>[] o) {
if (a.length != o.length)
return false;
for (int i = 0; i < a.length; i++)
if (!a[i].equals(o[i]) && !a[i].isAssignableFrom(o[i]))
return false;
return true;
}
public Object getHandle(Object obj) {
try {
return getMethod("getHandle", obj.getClass()).invoke(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Object invokeMethod(String method, Object obj) {
try {
return getMethod(method, obj.getClass()).invoke(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Object invokeMethodWithArgs(String method, Object obj, Object... args) {
try {
return getMethod(method, obj.getClass()).invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Method getMethod(String name, Class<?> clazz,
Class<?>... paramTypes) {
Class<?>[] t = toPrimitiveTypeArray(paramTypes);
for (Method m : clazz.getMethods()) {
Class<?>[] types = toPrimitiveTypeArray(m.getParameterTypes());
if (m.getName().equals(name) && equalsTypeArray(types, t))
return m;
}
return null;
}
public String getVersion() {
String name = Bukkit.getServer().getClass().getPackage().getName();
return name.substring(name.lastIndexOf('.') + 1) + ".";
}
public Class<?> getNMSClass(String className) {
String fullName = "net.minecraft.server." + getVersion() + className;
Class<?> clazz = null;
try {
clazz = Class.forName(fullName);
} catch (Exception e) {
e.printStackTrace();
}
return clazz;
}
public Field getField(Class<?> clazz, String name) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static boolean set(Object object, String fieldName, Object fieldValue) {
Class<?> clazz = object.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, fieldValue);
return true;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
return false;
}
public Object getPlayerField(Player player, String name) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Method getHandle = player.getClass().getMethod("getHandle");
Object nmsPlayer = getHandle.invoke(player);
Field field = nmsPlayer.getClass().getField(name);
return field.get(nmsPlayer);
}
public Method getMethod(Class<?> clazz, String name, Class<?>... args) {
for (Method m : clazz.getMethods())
if (m.getName().equals(name)
&& (args.length == 0 || ClassListEqual(args,
m.getParameterTypes()))) {
m.setAccessible(true);
return m;
}
return null;
}
public boolean ClassListEqual(Class<?>[] l1, Class<?>[] l2) {
boolean equal = true;
if (l1.length != l2.length)
return false;
for (int i = 0; i < l1.length; i++)
if (l1[i] != l2[i]) {
equal = false;
break;
}
return equal;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment