From b3d3921f6ecea0c446e1b4ba2ac0a068680ed9bc Mon Sep 17 00:00:00 2001 From: mmxw11 Date: Mon, 25 Sep 2017 18:04:39 +0300 Subject: [PATCH] Support item provider on a 1.8 server ... 1.9 is on the way --- .../BukkitInvContainerUpdateTask.java | 39 ++++- .../BukkitInvContainerItemProvider.java | 145 +++++++++++++++++- .../storage/InvItemStorage.java | 2 + 3 files changed, 180 insertions(+), 6 deletions(-) diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/protocol1_12to1_11_1/BukkitInvContainerUpdateTask.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/protocol1_12to1_11_1/BukkitInvContainerUpdateTask.java index 5f6d9e977..39068f2d8 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/protocol1_12to1_11_1/BukkitInvContainerUpdateTask.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/protocol1_12to1_11_1/BukkitInvContainerUpdateTask.java @@ -1,22 +1,55 @@ package us.myles.ViaVersion.bukkit.protocol1_12to1_11_1; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + import us.myles.ViaVersion.bukkit.providers.BukkitInvContainerItemProvider; +import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage.InvItemStorage; public class BukkitInvContainerUpdateTask implements Runnable { private BukkitInvContainerItemProvider provider; - private UUID uuid; + private final UUID uuid; + private final List items; public BukkitInvContainerUpdateTask(BukkitInvContainerItemProvider provider, UUID uuid) { this.provider = provider; this.uuid = uuid; + this.items = Collections.synchronizedList(new ArrayList<>()); + } + + public void addItem(short windowId, short slotId, short anumber) { + InvItemStorage storage = new InvItemStorage(windowId, slotId, anumber); + items.add(storage); } @Override public void run() { - // TODO Auto-generated method stub - provider.onTaskExecuted(uuid); + Player p = Bukkit.getServer().getPlayer(uuid); + if (p == null) { + provider.onTaskExecuted(uuid); + return; + } + try { + synchronized (items) { + for (InvItemStorage storage : items) { + Object packet = provider.buildWindowClickPacket(p, storage); + boolean result = provider.sendPlayer(p, packet); + System.out.println("item result: " + result); + if (!result) { + break; + } + } + System.out.println("items: " + items); + items.clear(); + } + } finally { + provider.onTaskExecuted(uuid); + } } } \ No newline at end of file diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/providers/BukkitInvContainerItemProvider.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/providers/BukkitInvContainerItemProvider.java index a02078e3b..7fad14feb 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/providers/BukkitInvContainerItemProvider.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/providers/BukkitInvContainerItemProvider.java @@ -1,28 +1,167 @@ package us.myles.ViaVersion.bukkit.providers; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.protocol.ProtocolRegistry; import us.myles.ViaVersion.bukkit.protocol1_12to1_11_1.BukkitInvContainerUpdateTask; +import us.myles.ViaVersion.bukkit.util.NMSUtil; import us.myles.ViaVersion.protocols.base.ProtocolInfo; import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.providers.InvContainerItemProvider; +import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage.InvItemStorage; +import us.myles.ViaVersion.util.ReflectionUtil; public class BukkitInvContainerItemProvider extends InvContainerItemProvider { private static Map updateTasks = new ConcurrentHashMap<>(); + private boolean supported; + // packet class + private Class wclickPacketClass; + // Use for nms + private Method nmsItemMethod; + private Method ephandle; + private Field connection; + private Method packetMethod; + + public BukkitInvContainerItemProvider() { + this.supported = isSupported(); + setupReflection(); + } @Override public boolean registerInvClickPacket(short windowId, short slotId, short anumber, UserConnection uconnection) { + if (!supported) { + return false; + } ProtocolInfo info = uconnection.get(ProtocolInfo.class); - // TODO: lets add some stuff here :) + UUID uuid = info.getUuid(); + BukkitInvContainerUpdateTask utask = updateTasks.get(uuid); + final boolean registered = utask != null; + if (!registered) { + utask = new BukkitInvContainerUpdateTask(this, uuid); + updateTasks.put(uuid, utask); + } // http://wiki.vg/index.php?title=Protocol&oldid=13223#Click_Window - System.out.println("info: " + info + " QUICK ACTION windowId: " + windowId + " slotId: " + slotId + " button: " + 0 + " anumber: " + anumber + " mode: " + 1); - return false; // change to true once supported + utask.addItem(windowId, slotId, anumber); + if (!registered) { + scheduleTask(utask); + } + return true; + } + + public Object buildWindowClickPacket(Player p, InvItemStorage storage) { + if (!supported) { + return null; + } + InventoryView inv = p.getOpenInventory(); + short slotId = storage.getSlotId(); + if (slotId > inv.countSlots()) { + return null; // wrong container open? + } + ItemStack itemstack = inv.getItem(slotId); + if (itemstack == null) { + return null; + } + Object cinstance = null; + try { + Object nmsItem = nmsItemMethod.invoke(null, itemstack); + // TODO: PASSTHROUGH PROTOCOLS? (Make InvItemStorage inheritance from Item?) + cinstance = wclickPacketClass.newInstance(); + ReflectionUtil.set(cinstance, "a", (int) storage.getWindowId()); + ReflectionUtil.set(cinstance, "slot", (int) slotId); + ReflectionUtil.set(cinstance, "button", 0); // shift + left mouse click + ReflectionUtil.set(cinstance, "d", storage.getActionNumber()); + ReflectionUtil.set(cinstance, "item", nmsItem); + // TODO: THIS MUST BE AN ENUM on 1.9+ SERVERS!! + ReflectionUtil.set(cinstance, "shift", 1); // mode + } catch (Exception e) { + e.printStackTrace(); + } + return cinstance; + } + + public boolean sendPlayer(Player p, Object packet) { + if (packet == null) { + return false; + } + try { + Object entityPlayer = ephandle.invoke(p); + Object pconnection = connection.get(entityPlayer); + // send + packetMethod.invoke(pconnection, packet); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return false; + } + return true; } public void onTaskExecuted(UUID uuid) { updateTasks.remove(uuid); } + + private void scheduleTask(BukkitInvContainerUpdateTask utask) { + BukkitScheduler scheduler = Bukkit.getServer().getScheduler(); + Plugin instance = Bukkit.getServer().getPluginManager().getPlugin("ViaVersion"); + scheduler.runTaskLater(instance, utask, 5); // 5 ticks later. + } + + private void setupReflection() { + if (!supported) { + return; + } + try { + this.wclickPacketClass = NMSUtil.nms("PacketPlayInWindowClick"); + Class citemStack = NMSUtil.obc("inventory.CraftItemStack"); + this.nmsItemMethod = citemStack.getDeclaredMethod("asNMSCopy", ItemStack.class); + } catch (Exception e) { + this.supported = false; + return; + } + try { + this.ephandle = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle"); + } catch (NoSuchMethodException | ClassNotFoundException e) { + this.supported = false; + throw new RuntimeException("Couldn't find CraftPlayer", e); + } + try { + this.connection = NMSUtil.nms("EntityPlayer").getDeclaredField("playerConnection"); + } catch (NoSuchFieldException | ClassNotFoundException e) { + this.supported = false; + throw new RuntimeException("Couldn't find Player Connection", e); + } + try { + this.packetMethod = NMSUtil.nms("PlayerConnection").getDeclaredMethod("a", wclickPacketClass); + } catch (NoSuchMethodException | ClassNotFoundException e) { + this.supported = false; + throw new RuntimeException("Couldn't find CraftPlayer", e); + } + } + + private boolean isSupported() { + int protocolId = ProtocolRegistry.SERVER_PROTOCOL; + if (protocolId == 47) { + return true; // 1.8 + } /**else if (protocolId >= 107 && protocolId <= 110) { + return true; // 1.9 + } else if (protocolId == 210) { + return true; // 1.10 + } else if (protocolId == 315 || protocolId == 316) { + return true; // 1.11 + }*/ + // this is not needed on 1.12+ + return false; + } } \ No newline at end of file diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_12to1_11_1/storage/InvItemStorage.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_12to1_11_1/storage/InvItemStorage.java index d20fa9d0e..9deb38231 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_12to1_11_1/storage/InvItemStorage.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_12to1_11_1/storage/InvItemStorage.java @@ -2,8 +2,10 @@ package us.myles.ViaVersion.protocols.protocol1_12to1_11_1.storage; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; @AllArgsConstructor +@ToString @Getter public class InvItemStorage {