diff --git a/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java b/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java index b3fc0a4..bdd3bc2 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java +++ b/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java @@ -18,6 +18,9 @@ import java.util.stream.Stream; import com.palmergames.bukkit.towny.Towny; import com.wasteofplastic.askyblock.ASkyBlock; +import de.epiceric.shopchest.nms.Platform; +import de.epiceric.shopchest.nms.reflection.PlatformImpl; +import de.epiceric.shopchest.nms.reflection.ShopChestDebug; import org.bstats.bukkit.Metrics; import org.bstats.charts.AdvancedPie; import org.bstats.charts.SimplePie; @@ -82,6 +85,7 @@ public class ShopChest extends JavaPlugin { private static ShopChest instance; private Config config; + private Platform platform; private HologramFormat hologramFormat; private ShopCommand shopCommand; private Economy econ = null; @@ -188,12 +192,17 @@ public class ShopChest extends JavaPlugin { case "v1_16_R1": case "v1_16_R2": case "v1_16_R3": + platform = new PlatformImpl(new ShopChestDebug(getLogger(), this::debug, this::debug)); + break; + case "v1_17_R1": + platform = new de.epiceric.shopchest.nms.v1_17_R1.PlatformImpl(); break; default: debug("Server version not officially supported: " + Utils.getServerVersion() + "!"); - debug("Plugin may still work, but more errors are expected!"); + //debug("Plugin may still work, but more errors are expected!"); getLogger().warning("Server version not officially supported: " + Utils.getServerVersion() + "!"); - getLogger().warning("Plugin may still work, but more errors are expected!"); + //getLogger().warning("Plugin may still work, but more errors are expected!"); + getServer().getPluginManager().disablePlugin(this); } shopUtils = new ShopUtils(this); @@ -559,6 +568,10 @@ public class ShopChest extends JavaPlugin { return shopCreationThreadPool; } + public Platform getPlatform() { + return platform; + } + public HologramFormat getHologramFormat() { return hologramFormat; } diff --git a/plugin/src/main/java/de/epiceric/shopchest/nms/ArmorStandWrapper.java b/plugin/src/main/java/de/epiceric/shopchest/nms/ArmorStandWrapper.java index 56ea9c4..0bf269c 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/nms/ArmorStandWrapper.java +++ b/plugin/src/main/java/de/epiceric/shopchest/nms/ArmorStandWrapper.java @@ -1,148 +1,57 @@ package de.epiceric.shopchest.nms; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import de.epiceric.shopchest.ShopChest; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; import java.util.UUID; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver; - -import de.epiceric.shopchest.ShopChest; -import de.epiceric.shopchest.utils.Utils; - public class ArmorStandWrapper { - private final NMSClassResolver nmsClassResolver = new NMSClassResolver(); - private final Class packetDataSerializerClass = nmsClassResolver.resolveSilent("network.PacketDataSerializer"); - private final Class packetPlayOutEntityDestroyClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityDestroy"); - private final Class packetPlayOutEntityMetadataClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityMetadata"); - private final Class packetPlayOutEntityTeleportClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityTeleport"); - private final Class dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher"); private final UUID uuid = UUID.randomUUID(); - private final int entityId; + private final FakeArmorStand fakeArmorStand; - private ShopChest plugin; private Location location; private String customName; public ArmorStandWrapper(ShopChest plugin, Location location, String customName) { - this.plugin = plugin; this.location = location; this.customName = customName; - this.entityId = Utils.getFreeEntityId(); + this.fakeArmorStand = plugin.getPlatform().createFakeArmorStand(); } public void setVisible(Player player, boolean visible) { - try { - if (visible) { - Object dataWatcher = Utils.createDataWatcher(customName, null); - Utils.sendPacket(plugin, Utils.createPacketSpawnEntity(plugin, entityId, uuid, location, EntityType.ARMOR_STAND), player); - Utils.sendPacket(plugin, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class) - .newInstance(entityId, dataWatcher, true), player); - } else if (entityId != -1) { - Utils.sendPacket(plugin, packetPlayOutEntityDestroyClass.getConstructor(int[].class).newInstance((Object) new int[]{entityId}), player); - } - } catch (ReflectiveOperationException e) { - plugin.getLogger().severe("Could not change hologram visibility"); - plugin.debug("Could not change armor stand visibility"); - plugin.debug(e); + final List receiver = Collections.singletonList(player); + if(visible){ + fakeArmorStand.spawn(uuid, location, receiver); + fakeArmorStand.sendData(customName, receiver); + } + else if(fakeArmorStand.getEntityId() != -1){ + fakeArmorStand.remove(receiver); } } public void setLocation(Location location) { this.location = location; - double y = location.getY() + (Utils.getServerVersion().equals("v1_8_R1") ? 0 : 1.975); - Object packet; - - try { - if (Utils.getMajorVersion() >= 17) { - // Empty constructor does not exist anymore in 1.17+ so create packet via serializer - Class byteBufClass = Class.forName("io.netty.buffer.ByteBuf"); - Class unpooledClass = Class.forName("io.netty.buffer.Unpooled"); - Object buffer = unpooledClass.getMethod("buffer").invoke(null); - Object serializer = packetDataSerializerClass.getConstructor(byteBufClass).newInstance(buffer); - - Method d = packetDataSerializerClass.getMethod("d", int.class); - Method writeDouble = packetDataSerializerClass.getMethod("writeDouble", double.class); - Method writeByte = packetDataSerializerClass.getMethod("writeByte", int.class); - Method writeBoolean = packetDataSerializerClass.getMethod("writeBoolean", boolean.class); - - d.invoke(serializer, getEntityId()); - writeDouble.invoke(serializer, location.getX()); - writeDouble.invoke(serializer, y); - writeDouble.invoke(serializer, location.getZ()); - writeByte.invoke(serializer, 0); - writeByte.invoke(serializer, 0); - writeBoolean.invoke(serializer, false); - - packet = packetPlayOutEntityTeleportClass.getConstructor(packetDataSerializerClass).newInstance(serializer); - } else { - packet = packetPlayOutEntityTeleportClass.getConstructor().newInstance(); - Field[] fields = packetPlayOutEntityTeleportClass.getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); - } - - boolean isPre9 = Utils.getMajorVersion() < 9; - fields[0].set(packet, entityId); - - if (isPre9) { - fields[1].set(packet, (int)(location.getX() * 32)); - fields[2].set(packet, (int)(y * 32)); - fields[3].set(packet, (int)(location.getZ() * 32)); - } else { - fields[1].set(packet, location.getX()); - fields[2].set(packet, y); - fields[3].set(packet, location.getZ()); - } - fields[4].set(packet, (byte) 0); - fields[5].set(packet, (byte) 0); - fields[6].set(packet, true); - } - - if (packet == null) { - plugin.getLogger().severe("Could not set hologram location"); - plugin.debug("Could not set armor stand location: Packet is null"); - return; - } - - for (Player player : location.getWorld().getPlayers()) { - Utils.sendPacket(plugin, packet, player); - } - } catch (ReflectiveOperationException e) { - plugin.getLogger().severe("Could not set hologram location"); - plugin.debug("Could not set armor stand location"); - plugin.debug(e); - } + fakeArmorStand.setLocation(location, Objects.requireNonNull(location.getWorld()).getPlayers()); } public void setCustomName(String customName) { this.customName = customName; - Object dataWatcher = Utils.createDataWatcher(customName, null); - try { - Object packet = packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class) - .newInstance(entityId, dataWatcher, true); - - for (Player player : location.getWorld().getPlayers()) { - Utils.sendPacket(plugin, packet, player); - } - } catch (ReflectiveOperationException e) { - plugin.getLogger().severe("Could not set hologram text"); - plugin.debug("Could not set armor stand custom name"); - plugin.debug(e); - } + fakeArmorStand.sendData(customName, Objects.requireNonNull(location.getWorld()).getPlayers()); } public void remove() { - for (Player player : location.getWorld().getPlayers()) { + for (Player player : Objects.requireNonNull(location.getWorld()).getPlayers()) { setVisible(player, false); } } public int getEntityId() { - return entityId; + return fakeArmorStand.getEntityId(); } public UUID getUuid() { diff --git a/plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java b/plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java index 9acda03..4e2b73f 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java +++ b/plugin/src/main/java/de/epiceric/shopchest/shop/ShopItem.java @@ -1,59 +1,28 @@ package de.epiceric.shopchest.shop; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - +import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.nms.FakeItem; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver; -import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver; -import de.epiceric.shopchest.ShopChest; -import de.epiceric.shopchest.utils.Utils; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class ShopItem { - private final ShopChest plugin; // concurrent since update task is in async thread private final Set viewers = ConcurrentHashMap.newKeySet(); private final ItemStack itemStack; private final Location location; private final UUID uuid = UUID.randomUUID(); - private final int entityId; - - private final NMSClassResolver nmsClassResolver = new NMSClassResolver(); - private final OBCClassResolver obcClassResolver = new OBCClassResolver(); - private final Class packetPlayOutEntityDestroyClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityDestroy"); - private final Class packetPlayOutEntityVelocityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityVelocity"); - private final Class packetPlayOutEntityMetadataClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutEntityMetadata"); - private final Class dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher"); - private final Class vec3dClass = nmsClassResolver.resolveSilent("world.phys.Vec3D"); - private final Class craftItemStackClass = obcClassResolver.resolveSilent("inventory.CraftItemStack"); - private final Class nmsItemStackClass = nmsClassResolver.resolveSilent("world.item.ItemStack"); + private final FakeItem fakeItem; public ShopItem(ShopChest plugin, ItemStack itemStack, Location location) { - this.plugin = plugin; this.itemStack = itemStack; this.location = location; - this.entityId = Utils.getFreeEntityId(); - - Class[] requiredClasses = new Class[] { - nmsItemStackClass, craftItemStackClass, packetPlayOutEntityMetadataClass, dataWatcherClass, - packetPlayOutEntityDestroyClass, packetPlayOutEntityVelocityClass, - }; - - for (Class c : requiredClasses) { - if (c == null) { - plugin.debug("Failed to create shop item: Could not find all required classes"); - return; - } - } + this.fakeItem = plugin.getPlatform().createFakeItem(); } /** @@ -91,22 +60,10 @@ public class ShopItem { */ public void showPlayer(Player p, boolean force) { if (viewers.add(p.getUniqueId()) || force) { - try { - Object nmsItemStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, itemStack); - Object dataWatcher = Utils.createDataWatcher(null, nmsItemStack); - Utils.sendPacket(plugin, Utils.createPacketSpawnEntity(plugin, entityId, uuid, location, EntityType.DROPPED_ITEM), p); - Utils.sendPacket(plugin, packetPlayOutEntityMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class).newInstance(entityId, dataWatcher, true), p); - if (Utils.getMajorVersion() < 14) { - Utils.sendPacket(plugin, packetPlayOutEntityVelocityClass.getConstructor(int.class, double.class, double.class, double.class).newInstance(entityId, 0D, 0D, 0D), p); - } else { - Object vec3d = vec3dClass.getConstructor(double.class, double.class, double.class).newInstance(0D, 0D, 0D); - Utils.sendPacket(plugin, packetPlayOutEntityVelocityClass.getConstructor(int.class, vec3dClass).newInstance(entityId, vec3d), p); - } - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException | InstantiationException e) { - plugin.getLogger().severe("Failed to create item!"); - plugin.debug("Failed to create item!"); - plugin.debug(e); - } + final List receiver = Collections.singletonList(p); + fakeItem.spawn(uuid, location, receiver); + fakeItem.sendData(itemStack, receiver); + fakeItem.resetVelocity(receiver); } } @@ -123,15 +80,8 @@ public class ShopItem { */ public void hidePlayer(Player p, boolean force) { if (viewers.remove(p.getUniqueId()) || force) { - try { - if (p.isOnline()) { - Object packetPlayOutEntityDestroy = packetPlayOutEntityDestroyClass.getConstructor(int[].class).newInstance((Object) new int[]{entityId}); - Utils.sendPacket(plugin, packetPlayOutEntityDestroy, p); - } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { - plugin.getLogger().severe("Failed to destroy shop item"); - plugin.debug("Failed to destroy shop item with reflection"); - plugin.debug(e); + if (p.isOnline()) { + fakeItem.remove(Collections.singletonList(p)); } } }