Skip to content

Commit f7e0e83

Browse files
committed
v9.7.0
Added XWorldBorder API. ReflectionUtils The getClass methods now throw a runtime exception instead of returning null and printing the stacktrace manually. SkullUtils Fixed an error in outdated versions. NMSExtras Added various entity data watcher methods. Added spinEntity()
1 parent 5ac9e6a commit f7e0e83

File tree

8 files changed

+779
-71
lines changed

8 files changed

+779
-71
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.github.cryptomorin</groupId>
88
<artifactId>XSeries</artifactId>
9-
<version>9.6.1.1</version>
9+
<version>9.7.0</version>
1010

1111
<name>XSeries</name>
1212
<description>A set of utilities for Minecraft plugins</description>

src/main/java/com/cryptomorin/xseries/NMSExtras.java

Lines changed: 157 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import org.bukkit.Location;
2727
import org.bukkit.Material;
2828
import org.bukkit.block.Block;
29+
import org.bukkit.entity.Entity;
2930
import org.bukkit.entity.LivingEntity;
3031
import org.bukkit.entity.Player;
3132

33+
import javax.annotation.Nullable;
3234
import java.lang.invoke.MethodHandle;
3335
import java.lang.invoke.MethodHandles;
3436
import java.lang.invoke.MethodType;
@@ -49,6 +51,7 @@
4951
*/
5052
public final class NMSExtras {
5153
public static final Class<?> EntityLivingClass = getNMSClass("world.entity", "EntityLiving");
54+
private static final MethodHandle GET_ENTITY_HANDLE;
5255
public static final MethodHandle EXP_PACKET;
5356
public static final MethodHandle ENTITY_PACKET;
5457
public static final MethodHandle WORLD_HANDLE, ENTITY_HANDLE;
@@ -96,9 +99,11 @@ public final class NMSExtras {
9699
MethodHandle tileEntitySign = null, tileEntitySign_getUpdatePacket = null, tileEntitySign_setLine = null, signText = null;
97100

98101
MethodHandle playOutMultiBlockChange = null, multiBlockChangeInfo = null, chunkWrapper = null, chunkWrapperSet = null,
99-
shortsOrInfo = null, setBlockData = null, getDataWatcher = null, dataWatcherGetItem = null, dataWatcherSetItem = null;
102+
shortsOrInfo = null, setBlockData = null, getDataWatcher = null, dataWatcherGetItem = null,
103+
dataWatcherSetItem = null, getHandle = null;
100104

101105
try {
106+
Class<?> CraftEntityClass = getCraftClass("entity.CraftEntity");
102107
Class<?> nmsEntityType = getNMSClass("world.entity", "EntityTypes");
103108
Class<?> nmsEntity = getNMSClass("world.entity", "Entity");
104109
Class<?> craftEntity = getCraftClass("entity.CraftEntity");
@@ -115,9 +120,10 @@ public final class NMSExtras {
115120
Class<?> DataWatcherItemClass = getNMSClass("network.syncher", "DataWatcher$Item");
116121
Class<?> DataWatcherObjectClass = getNMSClass("network.syncher", "DataWatcherObject");
117122

118-
// getDataWatcher = lookup.findVirtual(EntityLivingClass, "al", MethodType.methodType(DataWatcherClass));
119-
// dataWatcherGetItem = lookup.findVirtual(DataWatcherClass, "b", MethodType.methodType(Object.class, DataWatcherObjectClass)); // private <T> Item<T> c(DataWatcherObject<T> datawatcherobject)
120-
// dataWatcherSetItem = lookup.findVirtual(DataWatcherClass, "b", MethodType.methodType(void.class, DataWatcherItemClass, Object.class)); // private <T> Item<T> c(DataWatcherObject<T> datawatcherobject)
123+
getHandle = lookup.findVirtual(CraftEntityClass, "getHandle", MethodType.methodType(nmsEntity));
124+
getDataWatcher = lookup.findVirtual(nmsEntity, v(20, 2, "al").v(19, "aj").v(18, "ai").orElse("getDataWatcher"), MethodType.methodType(DataWatcherClass)); // getEntityData()
125+
dataWatcherGetItem = lookup.findVirtual(DataWatcherClass, v(18, "b").orElse("get"), MethodType.methodType(Object.class, DataWatcherObjectClass)); // private <T> Item<T> c(DataWatcherObject<T> datawatcherobject)
126+
dataWatcherSetItem = lookup.findVirtual(DataWatcherClass, v(18, "b").orElse("set"), MethodType.methodType(void.class, DataWatcherObjectClass, Object.class)); // private <T> Item<T> c(DataWatcherObject<T> datawatcherobject)
121127

122128
getBukkitEntity = lookup.findVirtual(nmsEntity, "getBukkitEntity", MethodType.methodType(craftEntity));
123129
entityHandle = lookup.findVirtual(craftEntity, "getHandle", MethodType.methodType(nmsEntity));
@@ -245,6 +251,7 @@ public final class NMSExtras {
245251
ex.printStackTrace();
246252
}
247253

254+
GET_ENTITY_HANDLE = getHandle;
248255
GET_DATA_WATCHER = getDataWatcher;
249256
DATA_WATCHER_GET_ITEM = dataWatcherGetItem;
250257
DATA_WATCHER_SET_ITEM = dataWatcherSetItem;
@@ -339,66 +346,196 @@ public static void lightning(Collection<Player> players, Location location, bool
339346
}
340347
}
341348

342-
public static void getData(LivingEntity entity, DataWatcherItemType id) {
349+
public static Object getData(Object dataWatcher, Object dataWatcherObject) {
343350
try {
344-
Object dataWatcher = GET_DATA_WATCHER.invoke(entity);
345-
DATA_WATCHER_GET_ITEM.invoke(dataWatcher, id);
351+
return DATA_WATCHER_GET_ITEM.invoke(dataWatcher, dataWatcherObject);
346352
} catch (Throwable e) {
347353
throw new RuntimeException(e);
348354
}
349355
}
350356

351-
public static Object setData(LivingEntity entity, DataWatcherItemType id, Object value) {
357+
@Nullable
358+
public static Object getEntityHandle(Entity entity) {
359+
Objects.requireNonNull(entity, "Cannot get handle of null entity");
352360
try {
353-
Object dataWatcher = GET_DATA_WATCHER.invoke(entity);
354-
return DATA_WATCHER_SET_ITEM.invoke(dataWatcher, id, value);
361+
return GET_ENTITY_HANDLE.invoke(entity);
362+
} catch (Throwable throwable) {
363+
throwable.printStackTrace();
364+
return null;
365+
}
366+
}
367+
368+
public static Object getDataWatcher(Object handle) {
369+
try {
370+
return GET_DATA_WATCHER.invoke(handle);
371+
} catch (Throwable e) {
372+
throw new RuntimeException(e);
373+
}
374+
}
375+
376+
public static Object setData(Object dataWatcher, Object dataWatcherObject, Object value) {
377+
try {
378+
return DATA_WATCHER_SET_ITEM.invoke(dataWatcher, dataWatcherObject, value);
355379
} catch (Throwable e) {
356380
throw new RuntimeException(e);
357381
}
358382
}
359383

360-
public static Object getStaticField(Class<?> clazz, String name) {
384+
public static Object getStaticFieldIgnored(Class<?> clazz, String name) {
385+
return getStaticField(clazz, name, true);
386+
}
387+
388+
public static Object getStaticField(Class<?> clazz, String name, boolean silent) {
361389
try {
362390
Field field = clazz.getDeclaredField(name);
363391
field.setAccessible(true);
364392
return field.get(null);
365393
} catch (Throwable e) {
366-
throw new RuntimeException(e);
394+
if (!silent) throw new RuntimeException(e);
395+
else return null;
396+
}
397+
}
398+
399+
public enum EntityPose {
400+
STANDING("a"),
401+
FALL_FLYING("b"),
402+
SLEEPING("c"),
403+
SWIMMING("d"),
404+
SPIN_ATTACK("e"),
405+
CROUCHING("f"),
406+
LONG_JUMPING("g"),
407+
DYING("h"),
408+
CROAKING("i"),
409+
USING_TONGUE("j"),
410+
SITTING("k"),
411+
ROARING("l"),
412+
SNIFFING("m"),
413+
EMERGING("n"),
414+
DIGGING("o"),
415+
;
416+
417+
public final Object enumValue;
418+
private final boolean supported;
419+
420+
EntityPose(String fieldName) {
421+
boolean supported = true;
422+
Object enumValue = null;
423+
424+
try {
425+
Class<?> entityPose = getNMSClass("world.entity", "EntityPose");
426+
enumValue = entityPose.getDeclaredField(v(17, fieldName).orElse(name())).get(null);
427+
} catch (Throwable e) {
428+
supported = false;
429+
}
430+
431+
this.supported = supported;
432+
this.enumValue = enumValue;
433+
}
434+
435+
public boolean isSupported() {
436+
return supported;
437+
}
438+
439+
public Object getEnumValue() {
440+
return enumValue;
367441
}
368442
}
369443

370444
public enum DataWatcherItemType {
371445
// protected static final DataWatcherObject<Byte> DATA_LIVING_ENTITY_FLAGS = DataWatcher.defineId(EntityLiving.class, DataWatcherRegistry.BYTE);
372-
DATA_LIVING_ENTITY_FLAGS(getStaticField(EntityLivingClass, "t"));
446+
DATA_LIVING_ENTITY_FLAGS(getStaticFieldIgnored(EntityLivingClass, "t"));
373447

374448
private final Object id;
375449

450+
private final boolean supported;
451+
376452
DataWatcherItemType(Object DataWatcherObject) {
453+
boolean supported = true;
454+
Object id = null;
455+
377456
try {
378457
// public int a() { return this.a; }
379458
// Method idMethod = DataWatcherObject.getClass().getMethod("a");
380-
this.id = DataWatcherObject;
459+
id = DataWatcherObject;
381460
} catch (Throwable e) {
382-
throw new RuntimeException(e);
461+
supported = false;
383462
}
463+
464+
this.supported = supported;
465+
this.id = id;
466+
}
467+
468+
public boolean isSupported() {
469+
return supported;
384470
}
385471

386472
public Object getId() {
387473
return id;
388474
}
389475
}
390476

391-
public static void spinEntity(LivingEntity entity, float ticks) {
477+
public enum LivingEntityFlags {
478+
SPIN_ATTACK(0x04);
479+
480+
private final byte bit;
481+
482+
LivingEntityFlags(int bit) {
483+
this.bit = (byte) bit;
484+
}
485+
486+
public byte getBit() {
487+
return bit;
488+
}
489+
}
490+
491+
public static void spinEntity(LivingEntity entity, boolean enabled) {
492+
if (!EntityPose.SPIN_ATTACK.isSupported()) {
493+
throw new UnsupportedOperationException("Spin attacks are not supported in " + getVersionInformation());
494+
}
495+
496+
// https://www.spigotmc.org/threads/trident-spinning-animation-riptide.426086/
497+
// https://www.spigotmc.org/threads/using-the-riptide-animation.469207/
392498
// https://wiki.vg/Entity_metadata#Living_Entity
393499
// Referenced as "Riptiding" or "AutoSpinAttack" modes in code.
394500
// EntityLiving.r(int ticks) doesn't exist in newer versions.
395-
// EntityLiving entityLiv = ((CraftPlayer) entity).getHandle();
396-
// DataWatcher dataWatcher = entityLiv.al();
397-
// dataWatcher.b((DataWatcherObject<Byte>) DataWatcherItemType.DATA_LIVING_ENTITY_FLAGS.getId(), (byte) 0x04);
501+
502+
// EntityLiving entityLiv = ((CraftPlayer) entity).getHandle();
503+
// DataWatcher dataWatcher = entityLiv.al();
504+
// dataWatcher.b((DataWatcherObject<Byte>) DataWatcherItemType.DATA_LIVING_ENTITY_FLAGS.getId(), (byte) 0x04);
505+
506+
setLivingEntityFlag(entity, LivingEntityFlags.SPIN_ATTACK.getBit(), enabled);
507+
}
508+
509+
public static void setLivingEntityFlag(Entity entity, int index, boolean flag) {
510+
Object handle = getEntityHandle(entity);
511+
Object dataWatcher = getDataWatcher(handle);
512+
513+
Object flagItem = DataWatcherItemType.DATA_LIVING_ENTITY_FLAGS.getId();
514+
byte currentFlags = (byte) getData(dataWatcher, flagItem);
515+
int newFlags;
516+
517+
if (flag) {
518+
newFlags = currentFlags | index;
519+
} else {
520+
newFlags = currentFlags & ~index;
521+
}
522+
523+
setData(dataWatcher, flagItem, (byte) newFlags);
524+
}
525+
526+
public static boolean hasLivingEntityFlag(Entity entity, int index) {
527+
Object handle = getEntityHandle(entity);
528+
Object dataWatcher = getDataWatcher(handle);
529+
byte flags = (byte) getData(dataWatcher, DataWatcherItemType.DATA_LIVING_ENTITY_FLAGS.getId());
530+
return (flags & index) != 0;
531+
}
532+
533+
public boolean isAutoSpinAttack(LivingEntity entity) {
534+
return hasLivingEntityFlag(entity, LivingEntityFlags.SPIN_ATTACK.getBit());
398535
}
399536

400537
/**
401-
* For the trident riptide animation use {@link #spinEntity(LivingEntity, float)} instead.
538+
* For the trident riptide animation use {@link #spinEntity(LivingEntity, boolean)} instead.
402539
*/
403540
public static void animation(Collection<? extends Player> players, LivingEntity entity, Animation animation) {
404541
try {

src/main/java/com/cryptomorin/xseries/ReflectionUtils.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,7 @@ public static Class<?> getNMSClass(@Nullable String packageName, @Nonnull String
338338
try {
339339
return Class.forName(NMS_PACKAGE + name);
340340
} catch (ClassNotFoundException ex) {
341-
ex.printStackTrace();
342-
return null;
341+
throw new RuntimeException(ex);
343342
}
344343
}
345344

@@ -431,8 +430,7 @@ public static Class<?> getCraftClass(@Nonnull String name) {
431430
try {
432431
return Class.forName(CRAFTBUKKIT_PACKAGE + name);
433432
} catch (ClassNotFoundException ex) {
434-
ex.printStackTrace();
435-
return null;
433+
throw new RuntimeException(ex);
436434
}
437435
}
438436

0 commit comments

Comments
 (0)