Link plugin to nms sub modules

This commit is contained in:
Flowsqy 2021-12-29 21:31:27 +01:00
parent 7410f2088d
commit b46bd953c3
3 changed files with 47 additions and 175 deletions

View File

@ -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;
}

View File

@ -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<Player> 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() {

View File

@ -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<UUID> 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<Player> 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));
}
}
}