mirror of
https://github.com/Xiao-MoMi/Custom-Crops.git
synced 2025-12-28 19:39:20 +00:00
Add 1.21.4+ asp compatibility
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
package net.momirealms.customcrops.common.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ReflectionUtils {
|
||||
public static final Unsafe UNSAFE;
|
||||
public static final MethodHandles.Lookup LOOKUP;
|
||||
private static final MethodHandle methodHandle$MethodHandleNatives$refKindIsSetter;
|
||||
private static final MethodHandle methodHandle$constructor$MemberName;
|
||||
private static final MethodHandle methodHandle$MemberName$getReferenceKind;
|
||||
private static final MethodHandle methodHandle$MethodHandles$Lookup$getDirectField;
|
||||
|
||||
static {
|
||||
try {
|
||||
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
UNSAFE = (Unsafe) unsafeField.get(null);
|
||||
Field implLookup = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
|
||||
@SuppressWarnings("deprecation") Object base = UNSAFE.staticFieldBase(implLookup);
|
||||
@SuppressWarnings("deprecation") long offset = UNSAFE.staticFieldOffset(implLookup);
|
||||
LOOKUP = (MethodHandles.Lookup) UNSAFE.getObject(base, offset); // 获取神权lookup
|
||||
Class<?> clazz$MethodHandleNatives = Class.forName("java.lang.invoke.MethodHandleNatives");
|
||||
Class<?> clazz$MemberName = Class.forName("java.lang.invoke.MemberName");
|
||||
methodHandle$MethodHandleNatives$refKindIsSetter = LOOKUP.unreflect(clazz$MethodHandleNatives.getDeclaredMethod("refKindIsSetter", byte.class));
|
||||
methodHandle$constructor$MemberName = LOOKUP.unreflectConstructor(clazz$MemberName.getDeclaredConstructor(Field.class, boolean.class));
|
||||
methodHandle$MemberName$getReferenceKind = LOOKUP.unreflect(clazz$MemberName.getDeclaredMethod("getReferenceKind"));
|
||||
methodHandle$MethodHandles$Lookup$getDirectField = LOOKUP.unreflect(MethodHandles.Lookup.class.getDeclaredMethod("getDirectField", byte.class, Class.class, clazz$MemberName));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ReflectionUtils() {}
|
||||
|
||||
public static Class<?> getClazz(String... classes) {
|
||||
for (String className : classes) {
|
||||
Class<?> clazz = getClazz(className);
|
||||
if (clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Class<?> getClazz(String clazz) {
|
||||
try {
|
||||
return Class.forName(clazz);
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean classExists(@NotNull final String clazz) {
|
||||
try {
|
||||
Class.forName(clazz);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean methodExists(@NotNull final Class<?> clazz, @NotNull final String method, @NotNull final Class<?>... parameterTypes) {
|
||||
try {
|
||||
clazz.getMethod(method, parameterTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final String field) {
|
||||
try {
|
||||
return setAccessible(clazz.getDeclaredField(field));
|
||||
} catch (NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(@NotNull Class<?> clazz, @NotNull String... possibleNames) {
|
||||
List<String> possibleNameList = Arrays.asList(possibleNames);
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (possibleNameList.contains(field.getName())) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getInstanceDeclaredField(final Class<?> clazz, final int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getStaticDeclaredField(final Class<?> clazz, final Class<?> type, final int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredFieldBackwards(final Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (int j = fields.length - 1; j >= 0; j--) {
|
||||
Field field = fields[j];
|
||||
if (field.getType() == type) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getInstanceDeclaredField(@NotNull Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getDeclaredFields(final Class<?> clazz) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
fields.add(setAccessible(field));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getInstanceDeclaredFields(@NotNull Class<?> clazz) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getDeclaredFields(@NotNull final Class<?> clazz, @NotNull final Class<?> type) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
fields.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getInstanceDeclaredFields(@NotNull Class<?> clazz, @NotNull Class<?> type) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getInstanceMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getDeclaredMethod(final Class<?> clazz, Class<?> returnType, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getDeclaredMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) return setAccessible(method);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getStaticMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType()))
|
||||
return setAccessible(method);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getStaticMethod(final Class<?> clazz, Class<?> returnType, String[] possibleNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
for (String name : possibleNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Method getStaticMethod(final Class<?> clazz, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Method getMethodOrElseThrow(final Class<?> clazz, final String[] possibleMethodNames, final Class<?>[] parameterTypes) throws NoSuchMethodException {
|
||||
Method method = getMethod(clazz, possibleMethodNames, parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(possibleMethodNames) + " with parameters " +
|
||||
Arrays.toString(parameterTypes) + " in class " + clazz.getName());
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Method> getMethods(@NotNull Class<?> clazz, @NotNull Class<?> returnType, @NotNull Class<?>... parameterTypes) {
|
||||
List<Method> list = new ArrayList<>();
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (!returnType.isAssignableFrom(method.getReturnType()) // check type
|
||||
|| method.getParameterCount() != parameterTypes.length // check length
|
||||
) continue;
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
outer: {
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
list.add(method);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T extends AccessibleObject> T setAccessible(@NotNull final T o) {
|
||||
o.setAccessible(true);
|
||||
return o;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) {
|
||||
try {
|
||||
return clazz.getConstructor(parameterTypes);
|
||||
} catch (NoSuchMethodException | SecurityException ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getDeclaredConstructor(Class<?> clazz, Class<?>... parameterTypes) {
|
||||
try {
|
||||
return setAccessible(clazz.getDeclaredConstructor(parameterTypes));
|
||||
} catch (NoSuchMethodException | SecurityException ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getConstructor(Class<?> clazz, int index) {
|
||||
try {
|
||||
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
|
||||
if (index < 0 || index >= constructors.length) {
|
||||
throw new IndexOutOfBoundsException("Invalid constructor index: " + index);
|
||||
}
|
||||
return setAccessible(constructors[index]);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Constructor<?> getTheOnlyConstructor(Class<?> clazz) {
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
if (constructors.length != 1) {
|
||||
throw new RuntimeException("This class is expected to have only one constructor but it has " + constructors.length);
|
||||
}
|
||||
return constructors[0];
|
||||
}
|
||||
|
||||
public static MethodHandle unreflectGetter(Field field) throws IllegalAccessException {
|
||||
try {
|
||||
return LOOKUP.unreflectGetter(field);
|
||||
} catch (IllegalAccessException e) {
|
||||
field.setAccessible(true);
|
||||
return LOOKUP.unreflectGetter(field);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MethodHandle unreflectSetter(Field field) {
|
||||
try {
|
||||
return LOOKUP.unreflectSetter(field);
|
||||
} catch (IllegalAccessException e) {
|
||||
try { // 绕过final限制获取方法句柄
|
||||
Object memberName = methodHandle$constructor$MemberName.invoke(field, true);
|
||||
Object refKind = methodHandle$MemberName$getReferenceKind.invoke(memberName);
|
||||
methodHandle$MethodHandleNatives$refKindIsSetter.invoke(refKind);
|
||||
return (MethodHandle) methodHandle$MethodHandles$Lookup$getDirectField.invoke(LOOKUP, refKind, field.getDeclaringClass(), memberName);
|
||||
} catch (Throwable ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodHandle unreflectMethod(Method method) throws IllegalAccessException {
|
||||
try {
|
||||
return LOOKUP.unreflect(method);
|
||||
} catch (IllegalAccessException e) {
|
||||
method.setAccessible(true);
|
||||
return LOOKUP.unreflect(method);
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodHandle unreflectConstructor(Constructor<?> constructor) throws IllegalAccessException {
|
||||
try {
|
||||
return LOOKUP.unreflectConstructor(constructor);
|
||||
} catch (IllegalAccessException e) {
|
||||
constructor.setAccessible(true);
|
||||
return LOOKUP.unreflectConstructor(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
public static VarHandle findVarHandle(Class<?> clazz, String name, Class<?> type) {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(clazz, LOOKUP)
|
||||
.findVarHandle(clazz, name, type);
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static VarHandle findVarHandle(Field field) {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(field.getDeclaringClass(), LOOKUP)
|
||||
.findVarHandle(field.getDeclaringClass(), field.getName(), field.getType());
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":api"))
|
||||
compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT")
|
||||
compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT")
|
||||
compileOnly("com.flowpowered:flow-nbt:${rootProject.properties["flow_nbt_version"]}")
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -17,8 +17,13 @@
|
||||
|
||||
package net.momirealms.customcrops.bukkit.integration.adaptor.asp_r2;
|
||||
|
||||
import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent;
|
||||
import com.infernalsuite.aswm.api.world.SlimeWorld;
|
||||
import com.flowpowered.nbt.*;
|
||||
import com.flowpowered.nbt.stream.NBTInputStream;
|
||||
import com.flowpowered.nbt.stream.NBTOutputStream;
|
||||
import com.infernalsuite.asp.api.AdvancedSlimePaperAPI;
|
||||
import com.infernalsuite.asp.api.events.LoadSlimeWorldEvent;
|
||||
import com.infernalsuite.asp.api.world.SlimeChunk;
|
||||
import com.infernalsuite.asp.api.world.SlimeWorld;
|
||||
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
|
||||
import net.momirealms.customcrops.api.core.InternalRegistries;
|
||||
import net.momirealms.customcrops.api.core.block.CustomCropsBlock;
|
||||
@@ -28,14 +33,19 @@ import net.momirealms.customcrops.api.util.StringUtils;
|
||||
import net.momirealms.customcrops.api.util.TagUtils;
|
||||
import net.momirealms.customcrops.common.helper.GsonHelper;
|
||||
import net.momirealms.customcrops.common.util.Key;
|
||||
import net.momirealms.customcrops.common.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -44,38 +54,26 @@ import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SlimeWorldAdaptorR2 extends AbstractWorldAdaptor<SlimeWorld> implements Listener {
|
||||
private final Class<?> byteArrayTagClass = ReflectionUtils.getClazz("net{}kyori{}adventure{}nbt{}ByteArrayBinaryTag".replace("{}", "."));
|
||||
private final Method method$ByteArrayBinaryTag$byteArrayBinaryTag = ReflectionUtils.getStaticMethod(byteArrayTagClass, byteArrayTagClass, byte.class.arrayType());
|
||||
private final Method method$ByteArrayBinaryTag$value = ReflectionUtils.getMethod(byteArrayTagClass, byte.class.arrayType());
|
||||
|
||||
private final Function<String, SlimeWorld> getSlimeWorldFunction;
|
||||
public SlimeWorldAdaptorR2() {
|
||||
}
|
||||
|
||||
public SlimeWorldAdaptorR2(int version) {
|
||||
public byte[] byteArrayTagToBytes(Object byteArrayTag) {
|
||||
try {
|
||||
if (version == 1) {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin("SlimeWorldManager");
|
||||
Class<?> slimeClass = Class.forName("com.infernalsuite.aswm.api.SlimePlugin");
|
||||
Method method = slimeClass.getMethod("getWorld", String.class);
|
||||
this.getSlimeWorldFunction = (name) -> {
|
||||
try {
|
||||
return (SlimeWorld) method.invoke(plugin, name);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
} else if (version == 2) {
|
||||
Class<?> apiClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimePaperAPI");
|
||||
Object apiInstance = apiClass.getMethod("instance").invoke(null);
|
||||
Method method = apiClass.getMethod("getLoadedWorld", String.class);
|
||||
this.getSlimeWorldFunction = (name) -> {
|
||||
try {
|
||||
return (SlimeWorld) method.invoke(apiInstance, name);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported version: " + version);
|
||||
}
|
||||
return (byte[]) method$ByteArrayBinaryTag$value.invoke(byteArrayTag);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException("Failed to convert byte array tag to byte[]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object bytesToByteArrayTag(byte[] bytes) {
|
||||
try {
|
||||
return method$ByteArrayBinaryTag$byteArrayBinaryTag.invoke(null, (Object) bytes);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to convert byte array tag to byte[]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,32 +91,45 @@ public class SlimeWorldAdaptorR2 extends AbstractWorldAdaptor<SlimeWorld> implem
|
||||
|
||||
@Override
|
||||
public SlimeWorld getWorld(String worldName) {
|
||||
return getSlimeWorldFunction.apply(worldName);
|
||||
}
|
||||
|
||||
private CompoundMap createOrGetDataMap(SlimeWorld world) {
|
||||
Optional<CompoundTag> optionalCompoundTag = world.getExtraData().getAsCompoundTag("customcrops");
|
||||
CompoundMap ccDataMap;
|
||||
if (optionalCompoundTag.isEmpty()) {
|
||||
ccDataMap = new CompoundMap();
|
||||
world.getExtraData().getValue().put(new CompoundTag("customcrops", ccDataMap));
|
||||
} else {
|
||||
ccDataMap = optionalCompoundTag.get().getValue();
|
||||
}
|
||||
return ccDataMap;
|
||||
return AdvancedSlimePaperAPI.instance().getLoadedWorld(worldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldExtraData loadExtraData(SlimeWorld world) {
|
||||
CompoundMap ccDataMap = createOrGetDataMap(world);
|
||||
Object extraTag = world.getExtraData().get("customcrops-extra-data");
|
||||
CompoundMap ccDataMap = null;
|
||||
if (extraTag != null) {
|
||||
try {
|
||||
NBTInputStream nbtInputStream = new NBTInputStream(new ByteArrayInputStream(byteArrayTagToBytes(extraTag)), 0, ByteOrder.BIG_ENDIAN);
|
||||
ccDataMap = ((CompoundTag) nbtInputStream.readTag()).getValue();
|
||||
} catch (IOException e) {
|
||||
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to read extra data from custom crops tag", e);
|
||||
}
|
||||
}
|
||||
if (ccDataMap == null) {
|
||||
ccDataMap = new CompoundMap();
|
||||
}
|
||||
String json = Optional.ofNullable(ccDataMap.get("world-info")).map(tag -> tag.getAsStringTag().get().getValue()).orElse(null);
|
||||
return (json == null || json.equals("null")) ? WorldExtraData.empty() : GsonHelper.get().fromJson(json, WorldExtraData.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public void saveExtraData(CustomCropsWorld<SlimeWorld> world) {
|
||||
CompoundMap ccDataMap = createOrGetDataMap(world.world());
|
||||
ccDataMap.put(new StringTag("world-info", GsonHelper.get().toJson(world.extraData())));
|
||||
CompoundTag ccDataMap = new CompoundTag("", new CompoundMap());
|
||||
ccDataMap.getValue().put(new StringTag("world-info", GsonHelper.get().toJson(world.extraData())));
|
||||
byte[] data;
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
NBTOutputStream nbtOutputStream = new NBTOutputStream(baos, 0, ByteOrder.BIG_ENDIAN);
|
||||
nbtOutputStream.writeTag(ccDataMap);
|
||||
data = baos.toByteArray();
|
||||
nbtOutputStream.close();
|
||||
Map<String, Object> data2 = (Map) world.world().getExtraData();
|
||||
data2.put("customcrops-extra-data", bytesToByteArrayTag(data));
|
||||
} catch (IOException e) {
|
||||
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to save extra data to custom crops tag", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -131,16 +142,24 @@ public class SlimeWorldAdaptorR2 extends AbstractWorldAdaptor<SlimeWorld> implem
|
||||
@Override
|
||||
public CustomCropsChunk loadChunk(CustomCropsWorld<SlimeWorld> world, ChunkPos pos, boolean createIfNotExists) {
|
||||
long time1 = System.currentTimeMillis();
|
||||
CompoundMap ccDataMap = createOrGetDataMap(world.world());
|
||||
Tag<?> chunkTag = ccDataMap.get(pos.asString());
|
||||
SlimeChunk slimeChunk = world.world().getChunk(pos.x(), pos.z());
|
||||
if (slimeChunk == null) {
|
||||
return createIfNotExists ? world.createChunk(pos) : null;
|
||||
}
|
||||
Object extraTag = slimeChunk.getExtraData().get("customcrops-data");
|
||||
CompoundTag chunkTag = null;
|
||||
if (extraTag != null) {
|
||||
try {
|
||||
NBTInputStream nbtInputStream = new NBTInputStream(new ByteArrayInputStream(byteArrayTagToBytes(extraTag)), 0, ByteOrder.BIG_ENDIAN);
|
||||
chunkTag = ((CompoundTag) nbtInputStream.readTag());
|
||||
} catch (IOException e) {
|
||||
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to read extra data from custom crops tag", e);
|
||||
}
|
||||
}
|
||||
if (chunkTag == null) {
|
||||
return createIfNotExists ? world.createChunk(pos) : null;
|
||||
}
|
||||
Optional<CompoundTag> chunkCompoundTag = chunkTag.getAsCompoundTag();
|
||||
if (chunkCompoundTag.isEmpty()) {
|
||||
return createIfNotExists ? world.createChunk(pos) : null;
|
||||
}
|
||||
CustomCropsChunk chunk = tagToChunk(world, chunkCompoundTag.get());
|
||||
CustomCropsChunk chunk = tagToChunk(world, chunkTag);
|
||||
long time2 = System.currentTimeMillis();
|
||||
BukkitCustomCropsPlugin.getInstance().debug(() -> "Took " + (time2-time1) + "ms to load chunk " + pos);
|
||||
return chunk;
|
||||
@@ -149,15 +168,32 @@ public class SlimeWorldAdaptorR2 extends AbstractWorldAdaptor<SlimeWorld> implem
|
||||
@Override
|
||||
public void saveRegion(CustomCropsWorld<SlimeWorld> world, CustomCropsRegion region) {}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public void saveChunk(CustomCropsWorld<SlimeWorld> world, CustomCropsChunk chunk) {
|
||||
CompoundMap ccDataMap = createOrGetDataMap(world.world());
|
||||
ChunkPos chunkPos = chunk.chunkPos();
|
||||
SlimeChunk slimeChunk = world.world().getChunk(chunkPos.x(), chunkPos.z());
|
||||
if (slimeChunk == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> data = (Map) slimeChunk.getExtraData();
|
||||
SerializableChunk serializableChunk = toSerializableChunk(chunk);
|
||||
Runnable runnable = () -> {
|
||||
if (serializableChunk.canPrune()) {
|
||||
ccDataMap.remove(chunk.chunkPos().asString());
|
||||
data.remove("customcrops-data");
|
||||
} else {
|
||||
ccDataMap.put(chunkToTag(serializableChunk));
|
||||
CompoundTag chunkTag = chunkToTag(serializableChunk);
|
||||
byte[] bytes;
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
NBTOutputStream nbtOutputStream = new NBTOutputStream(baos, 0, ByteOrder.BIG_ENDIAN);
|
||||
nbtOutputStream.writeTag(chunkTag);
|
||||
bytes = baos.toByteArray();
|
||||
nbtOutputStream.close();
|
||||
data.put("customcrops-data", bytesToByteArrayTag(bytes));
|
||||
} catch (IOException e) {
|
||||
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to save chunk " + chunk.chunkPos() + " on world " + world.worldName(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Project settings
|
||||
# Rule: [major update].[feature update].[bug fix]
|
||||
project_version=3.6.46.1
|
||||
project_version=3.6.47
|
||||
config_version=43
|
||||
project_group=net.momirealms
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ dependencies {
|
||||
}
|
||||
implementation(project(":compatibility"))
|
||||
implementation(project(":compatibility-asp-r1"))
|
||||
// implementation(project(":compatibility-asp-r2"))
|
||||
implementation(project(":compatibility-asp-r2"))
|
||||
|
||||
implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}")
|
||||
implementation("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}")
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.momirealms.customcrops.api.integration.SeasonProvider;
|
||||
import net.momirealms.customcrops.bukkit.config.BukkitConfigManager;
|
||||
import net.momirealms.customcrops.bukkit.integration.adaptor.BukkitWorldAdaptor;
|
||||
import net.momirealms.customcrops.bukkit.integration.adaptor.asp_r1.SlimeWorldAdaptorR1;
|
||||
import net.momirealms.customcrops.bukkit.integration.adaptor.asp_r2.SlimeWorldAdaptorR2;
|
||||
import net.momirealms.customcrops.common.helper.VersionHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
@@ -59,17 +60,22 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
try {
|
||||
Class.forName("com.infernalsuite.aswm.api.SlimePlugin");
|
||||
SlimeWorldAdaptorR1 adaptor = new SlimeWorldAdaptorR1(1);
|
||||
adaptors.add(adaptor);
|
||||
this.adaptors.add(adaptor);
|
||||
Bukkit.getPluginManager().registerEvents(adaptor, plugin.getBootstrap());
|
||||
plugin.getPluginLogger().info("SlimeWorldManager hooked!");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) {
|
||||
SlimeWorldAdaptorR1 adaptor = new SlimeWorldAdaptorR1(2);
|
||||
adaptors.add(adaptor);
|
||||
this.adaptors.add(adaptor);
|
||||
Bukkit.getPluginManager().registerEvents(adaptor, plugin.getBootstrap());
|
||||
plugin.getPluginLogger().info("AdvancedSlimePaper hooked!");
|
||||
}
|
||||
} else {
|
||||
SlimeWorldAdaptorR2 adaptor = new SlimeWorldAdaptorR2();
|
||||
this.adaptors.add(adaptor);
|
||||
Bukkit.getPluginManager().registerEvents(adaptor, plugin.getBootstrap());
|
||||
plugin.getPluginLogger().info("AdvancedSlimePaper hooked!");
|
||||
}
|
||||
this.adaptors.add(new BukkitWorldAdaptor());
|
||||
this.seasonProvider = new SeasonProvider() {
|
||||
@@ -369,7 +375,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
|
||||
@Override
|
||||
public CustomCropsWorld<?> adapt(String name) {
|
||||
for (WorldAdaptor<?> adaptor : adaptors) {
|
||||
for (WorldAdaptor<?> adaptor : this.adaptors) {
|
||||
Object world = adaptor.getWorld(name);
|
||||
if (world != null) {
|
||||
return adaptor.adapt(world);
|
||||
|
||||
@@ -4,6 +4,7 @@ include(":plugin")
|
||||
include(":plugin:j21")
|
||||
include(":compatibility")
|
||||
include(":compatibility-asp-r1")
|
||||
include(":compatibility-asp-r2")
|
||||
//include(":compatibility-asp-r2")
|
||||
include(":compatibility-oraxen-r1")
|
||||
include(":compatibility-oraxen-r2")
|
||||
|
||||
Reference in New Issue
Block a user