From 05a8029c82c8e7506e66181a6e16a36c35207e97 Mon Sep 17 00:00:00 2001 From: Altiami Date: Tue, 23 Apr 2024 14:33:14 -0700 Subject: [PATCH] Modularize plugin compatibility as subsystem * Add compatibility for Train Carts * Other misc for future TODO and consistency --- settings.gradle.kts | 1 + .../BasePluginCompatibility.java | 110 ++++++++++++++++++ .../CitizensPluginCompatibility.java | 66 ----------- .../PluginCompatibilityCitizens.java | 48 ++++++++ .../PluginCompatibilityRegistry.java | 22 ++++ .../PluginCompatibilityTrainCarts.java | 80 +++++++++++++ .../BaseReflectionReferenceResolver.java | 42 +++++++ .../ClassReflectionReferenceResolver.java | 33 ++++++ .../FieldReflectionReferenceResolver.java | 37 ++++++ .../MethodReflectionReferenceResolver.java | 40 +++++++ .../nitori/config/NitoriConfig.java | 1 + .../nitori/core/ChunkMapMixin.java | 18 ++- .../nitori/core/MixinServerEntity.java | 5 - 13 files changed, 430 insertions(+), 73 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/BasePluginCompatibility.java delete mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/CitizensPluginCompatibility.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityCitizens.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityRegistry.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityTrainCarts.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/BaseReflectionReferenceResolver.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/ClassReflectionReferenceResolver.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/FieldReflectionReferenceResolver.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/MethodReflectionReferenceResolver.java diff --git a/settings.gradle.kts b/settings.gradle.kts index ee8fe86..a2a7ceb 100755 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ pluginManagement { includeBuild("build-logic") repositories { gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") } } diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/BasePluginCompatibility.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/BasePluginCompatibility.java new file mode 100644 index 0000000..763b281 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/BasePluginCompatibility.java @@ -0,0 +1,110 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility; + +import com.mojang.logging.LogUtils; +import org.bukkit.Bukkit; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; + +public abstract class BasePluginCompatibility { + + private final String[] pluginNames; + + private final AtomicBoolean reflectionResolutionAttempted = new AtomicBoolean(false); + + protected BasePluginCompatibility(String[] pluginNames) { + this.pluginNames = pluginNames; + } + + @Nullable + private Map collectPluginClassLoaders() { + var pluginManager = Bukkit.getPluginManager(); + HashMap pluginClassLoaders = new HashMap<>(this.pluginNames.length, 1.0f); + for (var pluginName : this.pluginNames) { + var plugin = pluginManager.getPlugin(pluginName); + if (plugin == null) { + return null; + } + pluginClassLoaders.put(pluginName, plugin.getClass().getClassLoader()); + } + return pluginClassLoaders; + } + + private void onPluginClassesNotFound(ReflectiveOperationException e) { + //TODO: move repeated text to generalized function + //TODO: Get mod name for prefix + LogUtils.getLogger().error( + "[Nitori] FAILED TO LOCATE CLASSES FOR `" + this.getClass().getName() + "`! I'll assume the related plugin(s) will otherwise hang and always run on main. Please update my intel!\n" + + "[Nitori] Exception follows:\n" + + e.toString() + ); + } + + private void onReferenceUseFailure(CompletionException e) { + //TODO: move repeated text to generalized function + //TODO: Get mod name for prefix + LogUtils.getLogger().error( + "[Nitori] FAILED TO USE REFLECTED REFERENCES FOR `" + this.getClass().getName() + "`! I'll assume the related plugin(s) will otherwise hang and always run on main. Please update my intel!\n" + + "[Nitori] Exception follows:\n" + + e.toString() + ); + } + + protected abstract void collectReferences(@Nonnull Map pluginClassLoaders) throws ReflectiveOperationException; + + boolean completePluginCompatibilityCondition(BooleanSupplier conditionCallback) { + synchronized (this) { // compatibility classes are singletons so syncing on itself is safe + if (!this.reflectionResolutionAttempted.getAcquire()) { + var thisClassName = this.getClass().getName(); + var pluginClassLoaders = collectPluginClassLoaders(); + if (pluginClassLoaders == null) { + return false; + } + try { + //TODO: move repeated text to generalized function + //TODO: Get mod name for prefix + LogUtils.getLogger().info("[Nitori] Resolving reflection references for " + thisClassName + "."); + collectReferences(pluginClassLoaders); + //TODO: move repeated text to generalized function + //TODO: Get mod name for prefix + LogUtils.getLogger().info("[Nitori] Resolved reflection references for " + thisClassName + "."); + } catch (ReflectiveOperationException e) { + onPluginClassesNotFound(e); + return true; + } finally { + this.reflectionResolutionAttempted.setRelease(true); + } + } + } + try { + return conditionCallback.getAsBoolean(); + } catch (IllegalStateException e) { + //TODO: move to generalized function + //TODO: Get mod name for prefix + LogUtils.getLogger().error("[Nitori] Hey, could you not forget to tell me how to resolve the reflection references??"); + throw e; + } catch (CompletionException e) { + onReferenceUseFailure(e); + return true; + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/CitizensPluginCompatibility.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/CitizensPluginCompatibility.java deleted file mode 100644 index 7f8380e..0000000 --- a/src/main/java/net/gensokyoreimagined/nitori/compatibility/CitizensPluginCompatibility.java +++ /dev/null @@ -1,66 +0,0 @@ -// Nitori Copyright (C) 2024 Gensokyo Reimagined -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -package net.gensokyoreimagined.nitori.compatibility; - -import com.mojang.logging.LogUtils; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerPlayer; -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; -import org.spongepowered.asm.mixin.Unique; - -import javax.annotation.Nullable; - -public class CitizensPluginCompatibility { - @Unique - private static final String CITIZENS_PLUGIN_NAME = "Citizens"; - - @Nullable - @Unique - private static Class citizensPluginCitizensEntityTrackerClass = null; - @Nullable - @Unique - private static Class citizensPluginEntityHumanNPCClass = null; - @Unique - private static boolean gensouHacks$attemptedCitizensSearch = false; - - @Unique - private static void findCitizensClassesIfNeeded(Plugin citizensPlugin) { - if (!gensouHacks$attemptedCitizensSearch) { - var citizensPluginClassLoader = citizensPlugin.getClass().getClassLoader(); - gensouHacks$attemptedCitizensSearch = true; - try { - citizensPluginCitizensEntityTrackerClass = Class.forName("net.citizensnpcs.nms.v1_20_R3.util.CitizensEntityTracker", false, citizensPluginClassLoader); - citizensPluginEntityHumanNPCClass = Class.forName("net.citizensnpcs.nms.v1_20_R3.entity.EntityHumanNPC", false, citizensPluginClassLoader); - } catch (ClassNotFoundException e) { - //TODO: Get mod name for prefix - LogUtils.getLogger().error("[Nitori] CITIZENS PLUGIN CLASSES NOT LOCATED! I'll assume Citizens will hang and always run on main. Please update my intel!"); - } - } - } - - @Unique - public static boolean shouldRedirectToMainThread(ChunkMap.TrackedEntity trackedEntity, ServerPlayer serverPlayer) { - var bukkitPluginManager = Bukkit.getPluginManager(); - if (bukkitPluginManager.isPluginEnabled(CITIZENS_PLUGIN_NAME)) { - findCitizensClassesIfNeeded(bukkitPluginManager.getPlugin(CITIZENS_PLUGIN_NAME)); - if (citizensPluginCitizensEntityTrackerClass == null || citizensPluginEntityHumanNPCClass == null) { - return true; - } - return citizensPluginCitizensEntityTrackerClass.isInstance(trackedEntity) && !citizensPluginEntityHumanNPCClass.isInstance(serverPlayer); - } - return false; - } -} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityCitizens.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityCitizens.java new file mode 100644 index 0000000..fcd2661 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityCitizens.java @@ -0,0 +1,48 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility; + +import net.gensokyoreimagined.nitori.compatibility.reflection.ClassReflectionReferenceResolver; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerPlayer; + +import javax.annotation.Nonnull; +import java.util.Map; + +public final class PluginCompatibilityCitizens extends BasePluginCompatibility { + private static final String CITIZENS_PLUGIN_NAME = "Citizens"; + + private static final ClassReflectionReferenceResolver citizensPluginCitizensEntityTrackerClassResolver = new ClassReflectionReferenceResolver("net.citizensnpcs.nms.v1_20_R3.util.CitizensEntityTracker"); + + private static final ClassReflectionReferenceResolver citizensPluginEntityHumanNPCClassResolver = new ClassReflectionReferenceResolver("net.citizensnpcs.nms.v1_20_R3.entity.EntityHumanNPC"); + + PluginCompatibilityCitizens() { + super(new String[]{CITIZENS_PLUGIN_NAME}); + } + + @Override + protected void collectReferences(@Nonnull Map pluginClassLoaders) throws ReflectiveOperationException { + var citizensClassLoader = pluginClassLoaders.get(CITIZENS_PLUGIN_NAME); + citizensPluginCitizensEntityTrackerClassResolver.accept(citizensClassLoader); + citizensPluginEntityHumanNPCClassResolver.accept(citizensClassLoader); + } + + public boolean shouldRedirectToMainThread(ChunkMap.TrackedEntity trackedEntity, ServerPlayer serverPlayer) { + return completePluginCompatibilityCondition( + () -> citizensPluginCitizensEntityTrackerClassResolver.getResolution().isInstance(trackedEntity) + && !citizensPluginEntityHumanNPCClassResolver.getResolution().isInstance(serverPlayer) + ); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityRegistry.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityRegistry.java new file mode 100644 index 0000000..f04d24e --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityRegistry.java @@ -0,0 +1,22 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility; + +public class PluginCompatibilityRegistry { + + public static final PluginCompatibilityCitizens CITIZENS = new PluginCompatibilityCitizens(); + + public static final PluginCompatibilityTrainCarts TRAIN_CARTS = new PluginCompatibilityTrainCarts(); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityTrainCarts.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityTrainCarts.java new file mode 100644 index 0000000..451acbf --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/PluginCompatibilityTrainCarts.java @@ -0,0 +1,80 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility; + +import net.gensokyoreimagined.nitori.compatibility.reflection.ClassReflectionReferenceResolver; +import net.gensokyoreimagined.nitori.compatibility.reflection.FieldReflectionReferenceResolver; +import net.gensokyoreimagined.nitori.compatibility.reflection.MethodReflectionReferenceResolver; +import net.minecraft.server.level.ChunkMap; + +import javax.annotation.Nonnull; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.concurrent.CompletionException; + +public final class PluginCompatibilityTrainCarts extends BasePluginCompatibility { + private static final String BK_COMMON_LIB_PLUGIN_NAME = "BKCommonLib"; + private static final String TRAIN_CARTS_PLUGIN_NAME = "Train_Carts"; + + private static final FieldReflectionReferenceResolver bkCommonLibPluginEntityTypingHandler_INSTANCEFieldResolver = new FieldReflectionReferenceResolver( + "com.bergerkiller.bukkit.common.internal.logic.EntityTypingHandler", + "INSTANCE" + ); + + private static final MethodReflectionReferenceResolver bkCommonLibPluginEntityTypingHandler_getEntityTrackerEntryHookMethodResolver = new MethodReflectionReferenceResolver( + "com.bergerkiller.bukkit.common.internal.logic.EntityTypingHandler", + "getEntityTrackerEntryHook", + Object.class + ); + + private static final MethodReflectionReferenceResolver bkCommonLibPluginEntityTrackerEntryHook_getControllerMethodResolver = new MethodReflectionReferenceResolver( + "com.bergerkiller.bukkit.common.internal.hooks.EntityTrackerEntryHook", + "getController" + ); + + private static final ClassReflectionReferenceResolver trainCartsMinecartMemberNetworkClassResolver = new ClassReflectionReferenceResolver("com.bergerkiller.bukkit.tc.controller.MinecartMemberNetwork"); + + PluginCompatibilityTrainCarts() { + super(new String[]{BK_COMMON_LIB_PLUGIN_NAME, TRAIN_CARTS_PLUGIN_NAME}); + } + + @Override + protected void collectReferences(@Nonnull Map pluginClassLoaders) throws ReflectiveOperationException { + var bkCommonLibClassLoader = pluginClassLoaders.get(BK_COMMON_LIB_PLUGIN_NAME); + var trainCartsClassLoader = pluginClassLoaders.get(TRAIN_CARTS_PLUGIN_NAME); + bkCommonLibPluginEntityTypingHandler_INSTANCEFieldResolver.accept(bkCommonLibClassLoader); + bkCommonLibPluginEntityTypingHandler_getEntityTrackerEntryHookMethodResolver.accept(bkCommonLibClassLoader); + bkCommonLibPluginEntityTrackerEntryHook_getControllerMethodResolver.accept(bkCommonLibClassLoader); + trainCartsMinecartMemberNetworkClassResolver.accept(trainCartsClassLoader); + } + + public boolean shouldRedirectToMainThread(ChunkMap.TrackedEntity trackedEntity) { + return completePluginCompatibilityCondition( + () -> { + try { + /*com.bergerkiller.bukkit.common.internal.logic.EntityTypingHandler*/var bkCommonLibEntityTypingHandlerInstance = bkCommonLibPluginEntityTypingHandler_INSTANCEFieldResolver.getResolution().get(null); + /*com.bergerkiller.bukkit.common.internal.hooks.EntityTrackerEntryHook*/var serverEntityBkCommonLibEntityTrackerEntryHook = bkCommonLibPluginEntityTypingHandler_getEntityTrackerEntryHookMethodResolver.getResolution().invoke(bkCommonLibEntityTypingHandlerInstance, trackedEntity); + if (serverEntityBkCommonLibEntityTrackerEntryHook == null) { + return false; + } + /*com.bergerkiller.bukkit.common.controller.EntityNetworkController*/var serverEntityBkCommonLibUncastedNetworkController = bkCommonLibPluginEntityTrackerEntryHook_getControllerMethodResolver.getResolution().invoke(serverEntityBkCommonLibEntityTrackerEntryHook); + return trainCartsMinecartMemberNetworkClassResolver.getResolution().isInstance(serverEntityBkCommonLibUncastedNetworkController); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new CompletionException(e); + } + } + ); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/BaseReflectionReferenceResolver.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/BaseReflectionReferenceResolver.java new file mode 100644 index 0000000..5e276e6 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/BaseReflectionReferenceResolver.java @@ -0,0 +1,42 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility.reflection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseReflectionReferenceResolver { + @Nullable + private T resolution; + + @Nonnull + protected abstract T resolve(ClassLoader classLoader) throws ReflectiveOperationException; + + public void accept(ClassLoader classLoader) throws ReflectiveOperationException { + this.resolution = resolve(classLoader); + } + + @Nonnull + public T getResolution() { + if (this.resolution == null) { + throw new IllegalStateException("Reflection reference has not been resolved!"); + } + return this.resolution; + } + + protected static Class resolveClass(ClassLoader classLoader, String classPath) throws ClassNotFoundException { + return Class.forName(classPath, false, classLoader); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/ClassReflectionReferenceResolver.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/ClassReflectionReferenceResolver.java new file mode 100644 index 0000000..70381c2 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/ClassReflectionReferenceResolver.java @@ -0,0 +1,33 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility.reflection; + +import org.jetbrains.annotations.NotNull; + +public class ClassReflectionReferenceResolver extends BaseReflectionReferenceResolver> { + + private final String classPath; + + public ClassReflectionReferenceResolver(String classPath) { + this.classPath = classPath; + } + + + @Override + @NotNull + protected Class resolve(ClassLoader classLoader) throws ClassNotFoundException { + return resolveClass(classLoader, this.classPath); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/FieldReflectionReferenceResolver.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/FieldReflectionReferenceResolver.java new file mode 100644 index 0000000..426dbae --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/FieldReflectionReferenceResolver.java @@ -0,0 +1,37 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility.reflection; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; + +public class FieldReflectionReferenceResolver extends BaseReflectionReferenceResolver { + + private final String classPath; + + private final String fieldName; + + public FieldReflectionReferenceResolver(String classPath, String fieldName) { + this.classPath = classPath; + this.fieldName = fieldName; + } + + @Override + @NotNull + protected Field resolve(ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException { + return resolveClass(classLoader, this.classPath).getField(this.fieldName); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/MethodReflectionReferenceResolver.java b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/MethodReflectionReferenceResolver.java new file mode 100644 index 0000000..2eabd67 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/compatibility/reflection/MethodReflectionReferenceResolver.java @@ -0,0 +1,40 @@ +// Nitori Copyright (C) 2024 Gensokyo Reimagined +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +package net.gensokyoreimagined.nitori.compatibility.reflection; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; + +public class MethodReflectionReferenceResolver extends BaseReflectionReferenceResolver { + + private final String classPath; + + private final String methodName; + + private final Class[] methodParameterClasses; + + public MethodReflectionReferenceResolver(String classPath, String methodName, Class... methodParameterClasses) { + this.classPath = classPath; + this.methodName = methodName; + this.methodParameterClasses = methodParameterClasses; + } + + @Override + @NotNull + protected Method resolve(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { + return resolveClass(classLoader, this.classPath).getMethod(this.methodName, this.methodParameterClasses); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/config/NitoriConfig.java b/src/main/java/net/gensokyoreimagined/nitori/config/NitoriConfig.java index 62c3812..715cc40 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/config/NitoriConfig.java +++ b/src/main/java/net/gensokyoreimagined/nitori/config/NitoriConfig.java @@ -33,6 +33,7 @@ //import org.simpleyaml.configuration.file.YamlFile; //import org.simpleyaml.exceptions.InvalidConfigurationException; +//TODO: everything lol public class NitoriConfig { /* diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/ChunkMapMixin.java b/src/main/java/net/gensokyoreimagined/nitori/core/ChunkMapMixin.java index 3934a2b..8a232c2 100755 --- a/src/main/java/net/gensokyoreimagined/nitori/core/ChunkMapMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/core/ChunkMapMixin.java @@ -15,6 +15,7 @@ package net.gensokyoreimagined.nitori.core; import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; +import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; @@ -22,9 +23,10 @@ //import net.gensokyoreimagined.nitori.config.NitoriConfig; import net.gensokyoreimagined.nitori.access.IMixinChunkMapAccess; import net.gensokyoreimagined.nitori.access.IMixinChunkMap_TrackedEntityAccess; -import net.gensokyoreimagined.nitori.compatibility.CitizensPluginCompatibility; +import net.gensokyoreimagined.nitori.compatibility.PluginCompatibilityRegistry; import net.gensokyoreimagined.nitori.tracker.MultithreadedTracker; import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerPlayerConnection; @@ -88,6 +90,7 @@ private void reassignEntityTrackers(CallbackInfo ci) { @Inject(method = "processTrackQueue", at = @At("HEAD"), cancellable = true) private void atProcessTrackQueueHead(CallbackInfo callbackInfo) { // Implementation of 0107-Multithreaded-Tracker.patch + //TODO: Restore config condition //if (NitoriConfig.enableAsyncEntityTracker) { if (this.gensouHacks$multithreadedTracker == null) { this.gensouHacks$multithreadedTracker = new MultithreadedTracker(this.level.chunkSource.entityTickingChunks, this.gensouHacks$trackerMainThreadTasks); @@ -135,7 +138,7 @@ private void reassignSeenBy(CallbackInfo ci) { @Redirect(method = "updatePlayers(Lcom/destroystokyo/paper/util/misc/PooledLinkedHashSets$PooledObjectLinkedOpenHashSet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkMap$TrackedEntity;updatePlayer(Lnet/minecraft/server/level/ServerPlayer;)V")) private void handleCitizensPluginTracking(ChunkMap.TrackedEntity self, ServerPlayer serverPlayer) { // Nitori - Citizens tracker must run on the main thread to avoid cyclic wait - if (CitizensPluginCompatibility.shouldRedirectToMainThread(self, serverPlayer)) { + if (PluginCompatibilityRegistry.CITIZENS.shouldRedirectToMainThread(self, serverPlayer)) { ((IMixinChunkMapAccess) (Object) ((ServerLevel) serverPlayer.level()).chunkSource.chunkMap).gensouHacks$runOnTrackerMainThread(() -> this.updatePlayer(serverPlayer) ); @@ -153,5 +156,16 @@ private void skipSpigotAsyncPlayerTrackerClear(String reason) {} // Mirai - we c @SuppressWarnings("EmptyMethod") @Redirect(method = "updatePlayer", at = @At(value = "INVOKE", target = "Lorg/spigotmc/AsyncCatcher;catchOp(Ljava/lang/String;)V")) private void skipSpigotAsyncPlayerTrackerUpdate(String reason) {} // Mirai - we can update async + + @Redirect(method = "updatePlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerEntity;addPairing(Lnet/minecraft/server/level/ServerPlayer;)V")) + private void handleTrainCartsPluginAddPairing(ServerEntity self, ServerPlayer serverPlayer) { + if (PluginCompatibilityRegistry.TRAIN_CARTS.shouldRedirectToMainThread((ChunkMap.TrackedEntity) (Object) this)) { + ((IMixinChunkMapAccess) (Object) ((ServerLevel) serverPlayer.level()).chunkSource.chunkMap).gensouHacks$runOnTrackerMainThread(() -> + self.addPairing(serverPlayer) + ); + } else { + self.addPairing(serverPlayer); + } + } } } diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/MixinServerEntity.java b/src/main/java/net/gensokyoreimagined/nitori/core/MixinServerEntity.java index c5953f3..67519c6 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/core/MixinServerEntity.java +++ b/src/main/java/net/gensokyoreimagined/nitori/core/MixinServerEntity.java @@ -40,7 +40,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import javax.annotation.Nullable; import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -51,10 +50,6 @@ public abstract class MixinServerEntity { @Shadow private Entity entity; - @Shadow - @Nullable - private List> trackedDataValues; - @Shadow public void sendPairingData(ServerPlayer serverplayer, Consumer> consumer) { throw new AssertionError("Mixin failed to apply!");