mirror of
https://github.com/amalthea-mc/ShopChest.git
synced 2024-12-22 17:11:46 +00:00
Add reflection implementation structure
This commit is contained in:
parent
278b4462f3
commit
fe27bd5e60
@ -23,6 +23,11 @@
|
||||
<artifactId>shopchest-nms-interface</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.inventivetalent</groupId>
|
||||
<artifactId>reflectionhelper</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,22 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class FakeArmorStandImpl extends FakeEntityImpl implements FakeArmorStand {
|
||||
|
||||
public FakeArmorStandImpl(ShopChestDebug debug) {
|
||||
super(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String name, Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Location location, Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FakeEntityImpl implements FakeEntity {
|
||||
|
||||
protected final int entityId;
|
||||
protected final ShopChestDebug debug;
|
||||
|
||||
public FakeEntityImpl(ShopChestDebug debug) {
|
||||
this.entityId = ReflectionUtils.getFreeEntityId();
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class FakeItemImpl extends FakeEntityImpl implements FakeItem {
|
||||
|
||||
public FakeItemImpl(ShopChestDebug debug) {
|
||||
super(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(ItemStack item, Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetVelocity(Iterable<Player> receivers) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.reflection.resolver.FieldResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JsonBuilder {
|
||||
|
||||
public static class Part {
|
||||
private String value;
|
||||
|
||||
public Part() {
|
||||
this("", true);
|
||||
}
|
||||
|
||||
public Part(Object value) {
|
||||
this(value, value instanceof CharSequence);
|
||||
}
|
||||
|
||||
public Part(Object value, boolean appendQuotes) {
|
||||
if (appendQuotes) {
|
||||
this.value = "\"" + value + "\"";
|
||||
} else {
|
||||
this.value = String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public PartArray toArray() {
|
||||
return new PartArray(this);
|
||||
}
|
||||
|
||||
public PartMap toMap() {
|
||||
PartMap map = new PartMap();
|
||||
map.setValue("text", new Part());
|
||||
map.setValue("extra", toArray());
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PartMap extends Part {
|
||||
private Map<String, Part> values = new HashMap<>();
|
||||
|
||||
public PartMap() {
|
||||
}
|
||||
|
||||
public PartMap(Map<String, Part> values) {
|
||||
this.values.putAll(values);
|
||||
}
|
||||
|
||||
public void setValue(String key, Part value) {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
public void removeValue(String key) {
|
||||
values.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringJoiner joiner = new StringJoiner(",", "{", "}");
|
||||
values.forEach((key, value) -> joiner.add("\"" + key + "\":" + value.toString()));
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartMap toMap() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PartArray extends Part {
|
||||
private List<Part> parts = new ArrayList<>();
|
||||
|
||||
public PartArray(Part... parts) {
|
||||
this.parts.addAll(Arrays.asList(parts));
|
||||
}
|
||||
|
||||
public void addPart(Part part) {
|
||||
parts.add(part);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringJoiner joiner = new StringJoiner(",", "[", "]");
|
||||
parts.forEach(part -> joiner.add(part.toString()));
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartArray toArray() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern PART_PATTERN = Pattern.compile("((§(?:[a-fA-Fk-oK-OrR0-9]|#[a-fA-F0-9]{6}))+)([^§]*)");
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("(§[a-fA-F0-9]){6}");
|
||||
|
||||
private Part rootPart;
|
||||
private ShopChestDebug debug;
|
||||
|
||||
private final NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
private Class<?> iChatBaseComponentClass = nmsClassResolver.resolveSilent("network.chat.IChatBaseComponent");
|
||||
private Class<?> packetPlayOutChatClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutChat");
|
||||
private Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
|
||||
private Class<?> chatMessageTypeClass;
|
||||
|
||||
public JsonBuilder(ShopChestDebug debug) {
|
||||
this.debug = debug;
|
||||
|
||||
if (ReflectionUtils.getMajorVersion() >= 16) {
|
||||
chatMessageTypeClass = nmsClassResolver.resolveSilent("network.chat.ChatMessageType");
|
||||
}
|
||||
|
||||
Class<?>[] requiredClasses = new Class<?>[] {
|
||||
iChatBaseComponentClass, packetPlayOutChatClass, chatSerializerClass
|
||||
};
|
||||
|
||||
for (Class<?> c : requiredClasses) {
|
||||
if (c == null) {
|
||||
debug.debug("Failed to instantiate JsonBuilder: Could not find all required classes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Part parse(String text) {
|
||||
Matcher hexMatcher = HEX_PATTERN.matcher(text);
|
||||
while (hexMatcher.find()) {
|
||||
String hexCode = hexMatcher.group(0).replace("§", "");
|
||||
text = text.replace(hexMatcher.group(0), "§#" + hexCode);
|
||||
}
|
||||
|
||||
Matcher matcher = PART_PATTERN.matcher(text);
|
||||
|
||||
if (!matcher.find()) {
|
||||
return new Part(text);
|
||||
}
|
||||
|
||||
matcher.reset();
|
||||
|
||||
PartArray array = new PartArray(new Part());
|
||||
int lastEndIndex = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
int startIndex = matcher.start();
|
||||
int endIndex = matcher.end();
|
||||
|
||||
if (lastEndIndex != startIndex) {
|
||||
String betweenMatches = text.substring(lastEndIndex, startIndex);
|
||||
array.addPart(new Part(betweenMatches));
|
||||
}
|
||||
|
||||
String format = matcher.group(1);
|
||||
String value = matcher.group(3);
|
||||
|
||||
PartMap part = new PartMap();
|
||||
part.setValue("text", new Part(value));
|
||||
|
||||
String[] formats = format.split("§");
|
||||
for (String f : formats) {
|
||||
switch (f.toLowerCase()) {
|
||||
case "":
|
||||
break;
|
||||
case "k":
|
||||
part.setValue("obuscated", new Part(true));
|
||||
break;
|
||||
case "l":
|
||||
part.setValue("bold", new Part(true));
|
||||
break;
|
||||
case "m":
|
||||
part.setValue("strikethrough", new Part(true));
|
||||
break;
|
||||
case "n":
|
||||
part.setValue("underlined", new Part(true));
|
||||
break;
|
||||
case "o":
|
||||
part.setValue("italic", new Part(true));
|
||||
break;
|
||||
case "r":
|
||||
part.removeValue("obfuscated");
|
||||
part.removeValue("bold");
|
||||
part.removeValue("strikethrough");
|
||||
part.removeValue("underlined");
|
||||
part.removeValue("italic");
|
||||
part.removeValue("color");
|
||||
break;
|
||||
default:
|
||||
if (f.startsWith("#")) {
|
||||
part.setValue("color", new Part(f));
|
||||
} else {
|
||||
part.setValue("color", new Part(ChatColor.getByChar(f).name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array.addPart(part);
|
||||
lastEndIndex = endIndex;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return rootPart.toString();
|
||||
}
|
||||
|
||||
public Part getRootPart() {
|
||||
return rootPart;
|
||||
}
|
||||
|
||||
public void setRootPart(Part rootPart) {
|
||||
this.rootPart = rootPart;
|
||||
}
|
||||
|
||||
public void sendJson(Player p) {
|
||||
try {
|
||||
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, toString());
|
||||
Object packetPlayOutChat = ReflectionUtils.getMajorVersion() < 16
|
||||
? packetPlayOutChatClass.getConstructor(iChatBaseComponentClass).newInstance(iChatBaseComponent)
|
||||
: packetPlayOutChatClass.getConstructor(iChatBaseComponentClass, chatMessageTypeClass, UUID.class)
|
||||
.newInstance(iChatBaseComponent, (new FieldResolver(chatMessageTypeClass)).resolve("CHAT", "a").get(null), UUID.randomUUID());
|
||||
|
||||
ReflectionUtils.sendPacket(debug, packetPlayOutChat, p);
|
||||
debug.debug("Sent JSON: " + toString());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
debug.getLogger().severe("Failed to send JSON with reflection");
|
||||
debug.debug("Failed to send JSON with reflection: " + toString());
|
||||
debug.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import de.epiceric.shopchest.nms.FakeArmorStand;
|
||||
import de.epiceric.shopchest.nms.FakeItem;
|
||||
import de.epiceric.shopchest.nms.Platform;
|
||||
|
||||
public class PlatformImpl implements Platform {
|
||||
|
||||
private final ShopChestDebug debug;
|
||||
|
||||
public PlatformImpl(ShopChestDebug debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FakeArmorStand createFakeArmorStand() {
|
||||
return new FakeArmorStandImpl(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeItem createFakeItem() {
|
||||
return new FakeItemImpl(debug);
|
||||
}
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.inventivetalent.reflection.resolver.FieldResolver;
|
||||
import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ReflectionUtils {
|
||||
static NMSClassResolver nmsClassResolver = new NMSClassResolver();
|
||||
static Class<?> entityClass = nmsClassResolver.resolveSilent("world.entity.Entity");
|
||||
static Class<?> entityArmorStandClass = nmsClassResolver.resolveSilent("world.entity.decoration.EntityArmorStand");
|
||||
static Class<?> entityItemClass = nmsClassResolver.resolveSilent("world.entity.item.EntityItem");
|
||||
static Class<?> dataWatcherClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcher");
|
||||
static Class<?> dataWatcherObjectClass = nmsClassResolver.resolveSilent("network.syncher.DataWatcherObject");
|
||||
static Class<?> chatSerializerClass = nmsClassResolver.resolveSilent("ChatSerializer", "network.chat.IChatBaseComponent$ChatSerializer");
|
||||
|
||||
private ReflectionUtils() {}
|
||||
|
||||
/**
|
||||
* Create a NMS data watcher object to send via a {@code PacketPlayOutEntityMetadata} packet.
|
||||
* Gravity will be disabled and the custom name will be displayed if available.
|
||||
* @param customName Custom Name of the entity or {@code null}
|
||||
* @param nmsItemStack NMS ItemStack or {@code null} if armor stand
|
||||
*/
|
||||
public static Object createDataWatcher(ShopChestDebug debug, String customName, Object nmsItemStack) {
|
||||
String version = getServerVersion();
|
||||
int majorVersion = getMajorVersion();
|
||||
|
||||
try {
|
||||
byte entityFlags = nmsItemStack == null ? (byte) 0b100000 : 0; // invisible if armor stand
|
||||
byte armorStandFlags = nmsItemStack == null ? (byte) 0b10000 : 0; // marker (since 1.8_R2)
|
||||
|
||||
Object dataWatcher = dataWatcherClass.getConstructor(entityClass).newInstance((Object) null);
|
||||
if (majorVersion < 9) {
|
||||
if (getRevision() == 1) armorStandFlags = 0; // Marker not supported on 1.8_R1
|
||||
|
||||
Method a = dataWatcherClass.getMethod("a", int.class, Object.class);
|
||||
a.invoke(dataWatcher, 0, entityFlags); // flags
|
||||
a.invoke(dataWatcher, 1, (short) 300); // air ticks (?)
|
||||
a.invoke(dataWatcher, 3, (byte) (customName != null ? 1 : 0)); // custom name visible
|
||||
a.invoke(dataWatcher, 2, customName != null ? customName : ""); // custom name
|
||||
a.invoke(dataWatcher, 4, (byte) 1); // silent
|
||||
a.invoke(dataWatcher, 10, nmsItemStack == null ? armorStandFlags : nmsItemStack); // item / armor stand flags
|
||||
} else {
|
||||
Method register = dataWatcherClass.getMethod("register", dataWatcherObjectClass, Object.class);
|
||||
String[] dataWatcherObjectFieldNames;
|
||||
|
||||
if ("v1_9_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"ax", "ay", "aA", "az", "aB", null, "c", "a"};
|
||||
} else if ("v1_9_R2".equals(version)){
|
||||
dataWatcherObjectFieldNames = new String[] {"ay", "az", "aB", "aA", "aC", null, "c", "a"};
|
||||
} else if ("v1_10_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"aa", "az", "aB", "aA", "aC", "aD", "c", "a"};
|
||||
} else if ("v1_11_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"Z", "az", "aB", "aA", "aC", "aD", "c", "a"};
|
||||
} else if ("v1_12_R1".equals(version) || "v1_12_R2".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"Z", "aA", "aC", "aB", "aD", "aE", "c", "a"};
|
||||
} else if ("v1_13_R1".equals(version) || "v1_13_R2".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"ac", "aD", "aF", "aE", "aG", "aH", "b", "a"};
|
||||
} else if ("v1_14_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"W", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
|
||||
} else if ("v1_15_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "aA", "az", "aB", "aC", "ITEM", "b"};
|
||||
} else if ("v1_16_R1".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"T", "AIR_TICKS", "ay", "ax", "az", "aA", "ITEM", "b"};
|
||||
} else if ("v1_16_R2".equals(version) || "v1_16_R3".equals(version)) {
|
||||
dataWatcherObjectFieldNames = new String[] {"S", "AIR_TICKS", "ar", "aq", "as", "at", "ITEM", "b"};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
Field fEntityFlags = entityClass.getDeclaredField(dataWatcherObjectFieldNames[0]);
|
||||
Field fAirTicks = entityClass.getDeclaredField(dataWatcherObjectFieldNames[1]);
|
||||
Field fNameVisible = entityClass.getDeclaredField(dataWatcherObjectFieldNames[2]);
|
||||
Field fCustomName = entityClass.getDeclaredField(dataWatcherObjectFieldNames[3]);
|
||||
Field fSilent = entityClass.getDeclaredField(dataWatcherObjectFieldNames[4]);
|
||||
Field fNoGravity = majorVersion >= 10 ? entityClass.getDeclaredField(dataWatcherObjectFieldNames[5]) : null;
|
||||
Field fItem = entityItemClass.getDeclaredField(dataWatcherObjectFieldNames[6]);
|
||||
Field fArmorStandFlags = entityArmorStandClass.getDeclaredField(dataWatcherObjectFieldNames[7]);
|
||||
|
||||
fEntityFlags.setAccessible(true);
|
||||
fAirTicks.setAccessible(true);
|
||||
fNameVisible.setAccessible(true);
|
||||
fCustomName.setAccessible(true);
|
||||
fSilent.setAccessible(true);
|
||||
if (majorVersion >= 10) fNoGravity.setAccessible(true);
|
||||
fItem.setAccessible(true);
|
||||
fArmorStandFlags.setAccessible(true);
|
||||
|
||||
register.invoke(dataWatcher, fEntityFlags.get(null), entityFlags);
|
||||
register.invoke(dataWatcher, fAirTicks.get(null), 300);
|
||||
register.invoke(dataWatcher, fNameVisible.get(null), customName != null);
|
||||
register.invoke(dataWatcher, fSilent.get(null), true);
|
||||
if (majorVersion < 13) register.invoke(dataWatcher, fCustomName.get(null), customName != null ? customName : "");
|
||||
|
||||
if (nmsItemStack != null) {
|
||||
register.invoke(dataWatcher, fItem.get(null), majorVersion < 11 ? com.google.common.base.Optional.of(nmsItemStack) : nmsItemStack);
|
||||
} else {
|
||||
register.invoke(dataWatcher, fArmorStandFlags.get(null), armorStandFlags);
|
||||
}
|
||||
|
||||
if (majorVersion >= 10) {
|
||||
register.invoke(dataWatcher, fNoGravity.get(null), true);
|
||||
if (majorVersion >= 13) {
|
||||
if (customName != null) {
|
||||
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, JsonBuilder.parse(customName).toString());
|
||||
register.invoke(dataWatcher, fCustomName.get(null), Optional.of(iChatBaseComponent));
|
||||
} else {
|
||||
register.invoke(dataWatcher, fCustomName.get(null), Optional.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataWatcher;
|
||||
} catch (InstantiationException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
|
||||
debug.getLogger().severe("Failed to create data watcher!");
|
||||
debug.debug("Failed to create data watcher");
|
||||
debug.debug(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a free entity ID for use in {@link #createPacketSpawnEntity(ShopChestDebug, int, UUID, Location, EntityType)}
|
||||
*
|
||||
* @return The id or {@code -1} if a free entity ID could not be retrieved.
|
||||
*/
|
||||
public static int getFreeEntityId() {
|
||||
try {
|
||||
Field entityCountField = new FieldResolver(entityClass).resolve("entityCount", "b");
|
||||
entityCountField.setAccessible(true);
|
||||
if (entityCountField.getType() == int.class) {
|
||||
int id = entityCountField.getInt(null);
|
||||
entityCountField.setInt(null, id+1);
|
||||
return id;
|
||||
} else if (entityCountField.getType() == AtomicInteger.class) {
|
||||
return ((AtomicInteger) entityCountField.get(null)).incrementAndGet();
|
||||
}
|
||||
|
||||
return -1;
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code PacketPlayOutSpawnEntity} object.
|
||||
* Only {@link EntityType#ARMOR_STAND} and {@link EntityType#DROPPED_ITEM} are supported!
|
||||
*/
|
||||
public static Object createPacketSpawnEntity(ShopChestDebug debug, int id, UUID uuid, Location loc, EntityType type) {
|
||||
try {
|
||||
Class<?> packetPlayOutSpawnEntityClass = nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayOutSpawnEntity");
|
||||
Class<?> entityTypesClass = nmsClassResolver.resolveSilent("world.entity.EntityTypes");
|
||||
|
||||
boolean isPre9 = getMajorVersion() < 9;
|
||||
boolean isPre14 = getMajorVersion() < 14;
|
||||
|
||||
double y = loc.getY();
|
||||
if (type == EntityType.ARMOR_STAND && !getServerVersion().equals("v1_8_R1")) {
|
||||
// Marker armor stand => lift by normal armor stand height
|
||||
y += 1.975;
|
||||
}
|
||||
|
||||
Object packet = packetPlayOutSpawnEntityClass.getConstructor().newInstance();
|
||||
|
||||
Field[] fields = new Field[12];
|
||||
fields[0] = packetPlayOutSpawnEntityClass.getDeclaredField("a"); // ID
|
||||
fields[1] = packetPlayOutSpawnEntityClass.getDeclaredField("b"); // UUID (Only 1.9+)
|
||||
fields[2] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "b" : "c"); // Loc X
|
||||
fields[3] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "c" : "d"); // Loc Y
|
||||
fields[4] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "d" : "e"); // Loc Z
|
||||
fields[5] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "e" : "f"); // Mot X
|
||||
fields[6] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "f" : "g"); // Mot Y
|
||||
fields[7] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "g" : "h"); // Mot Z
|
||||
fields[8] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "h" : "i"); // Pitch
|
||||
fields[9] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "i" : "j"); // Yaw
|
||||
fields[10] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "j" : "k"); // Type
|
||||
fields[11] = packetPlayOutSpawnEntityClass.getDeclaredField(isPre9 ? "k" : "l"); // Data
|
||||
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
|
||||
Object entityType = null;
|
||||
if (!isPre14) {
|
||||
entityType = entityTypesClass.getField(type == EntityType.ARMOR_STAND ? "ARMOR_STAND" : "ITEM").get(null);
|
||||
}
|
||||
|
||||
fields[0].set(packet, id);
|
||||
if (!isPre9) fields[1].set(packet, uuid);
|
||||
if (isPre9) {
|
||||
fields[2].set(packet, (int)(loc.getX() * 32));
|
||||
fields[3].set(packet, (int)(y * 32));
|
||||
fields[4].set(packet, (int)(loc.getZ() * 32));
|
||||
} else {
|
||||
fields[2].set(packet, loc.getX());
|
||||
fields[3].set(packet, y);
|
||||
fields[4].set(packet, loc.getZ());
|
||||
}
|
||||
fields[5].set(packet, 0);
|
||||
fields[6].set(packet, 0);
|
||||
fields[7].set(packet, 0);
|
||||
fields[8].set(packet, 0);
|
||||
fields[9].set(packet, 0);
|
||||
if (isPre14) fields[10].set(packet, type == EntityType.ARMOR_STAND ? 78 : 2);
|
||||
else fields[10].set(packet, entityType);
|
||||
fields[11].set(packet, 0);
|
||||
|
||||
return packet;
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||
debug.getLogger().severe("Failed to create packet to spawn entity!");
|
||||
debug.debug("Failed to create packet to spawn entity!");
|
||||
debug.debug(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to a player
|
||||
* @param debug An instance of the {@link ShopChestDebug} debug instance
|
||||
* @param packet Packet to send
|
||||
* @param player Player to which the packet should be sent
|
||||
* @return {@code true} if the packet was sent, or {@code false} if an exception was thrown
|
||||
*/
|
||||
public static boolean sendPacket(ShopChestDebug debug, Object packet, Player player) {
|
||||
try {
|
||||
if (packet == null) {
|
||||
debug.debug("Failed to send packet: Packet is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> packetClass = nmsClassResolver.resolveSilent("network.protocol.Packet");
|
||||
if (packetClass == null) {
|
||||
debug.debug("Failed to send packet: Could not find Packet class");
|
||||
return false;
|
||||
}
|
||||
|
||||
Object nmsPlayer = player.getClass().getMethod("getHandle").invoke(player);
|
||||
Field fConnection = (new FieldResolver(nmsPlayer.getClass())).resolve("playerConnection", "b");
|
||||
Object playerConnection = fConnection.get(nmsPlayer);
|
||||
|
||||
playerConnection.getClass().getMethod("sendPacket", packetClass).invoke(playerConnection, packet);
|
||||
|
||||
return true;
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
|
||||
debug.getLogger().severe("Failed to send packet " + packet.getClass().getName());
|
||||
debug.debug("Failed to send packet " + packet.getClass().getName());
|
||||
debug.debug(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current server version with revision number (e.g. v1_9_R2, v1_10_R1)
|
||||
*/
|
||||
public static String getServerVersion() {
|
||||
String packageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
return packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The revision of the current server version (e.g. <i>2</i> for v1_9_R2, <i>1</i> for v1_10_R1)
|
||||
*/
|
||||
public static int getRevision() {
|
||||
return Integer.parseInt(getServerVersion().substring(getServerVersion().length() - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The major version of the server (e.g. <i>9</i> for 1.9.2, <i>10</i> for 1.10)
|
||||
*/
|
||||
public static int getMajorVersion() {
|
||||
return Integer.parseInt(getServerVersion().split("_")[1]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package de.epiceric.shopchest.nms.reflection;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ShopChestDebug {
|
||||
|
||||
private final Logger logger;
|
||||
private final Consumer<String> debugConsumer;
|
||||
private final Consumer<Throwable> throwableConsumer;
|
||||
|
||||
public ShopChestDebug(Logger logger, Consumer<String> debugConsumer, Consumer<Throwable> throwableConsumer) {
|
||||
this.logger = logger;
|
||||
this.debugConsumer = debugConsumer;
|
||||
this.throwableConsumer = throwableConsumer;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public void debug(String message){
|
||||
debugConsumer.accept(message);
|
||||
}
|
||||
|
||||
public void debug(Throwable e){
|
||||
throwableConsumer.accept(e);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user