Merge branch 'dev-multithreaded-tracker'
This commit is contained in:
@@ -12,4 +12,4 @@ plugins {
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
rootProject.name = "GensouHacks"
|
||||
rootProject.name = "Nitori"
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.access;
|
||||
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IMixinChunkMapAccess {
|
||||
void gensouHacks$runOnTrackerMainThread(final Runnable runnable);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility;
|
||||
|
||||
import net.gensokyoreimagined.nitori.util.NitoriUtil;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Base class for plugin compatibility. Defines common functions for evaluating plugin compatibility conditions.
|
||||
* Any given instance of a subclass should always be a singleton. See {@link net.gensokyoreimagined.nitori.compatibility.PluginCompatibilityRegistry}
|
||||
*/
|
||||
public abstract class BasePluginCompatibility {
|
||||
|
||||
/**
|
||||
* List of plugin names required by this compatibility class. The names in this list are to be the same used in
|
||||
* the plugin.yml files in their respective plugins.
|
||||
*/
|
||||
private final String[] pluginNames;
|
||||
|
||||
/**
|
||||
* Whether the compatibility class has attempted to resolve the reflection references yet or not.
|
||||
*/
|
||||
private boolean reflectionResolutionAttempted = false;
|
||||
|
||||
/**
|
||||
* Define a new compatibility class.
|
||||
* @param pluginNames See {@link net.gensokyoreimagined.nitori.compatibility.BasePluginCompatibility#pluginNames}
|
||||
*/
|
||||
protected BasePluginCompatibility(String[] pluginNames) {
|
||||
this.pluginNames = pluginNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the class loaders for the plugins needed for this compatibility class.
|
||||
* @return The class loaders for the plugins needed for this compatibility class.
|
||||
*/
|
||||
@Nullable
|
||||
private Map<String, ClassLoader> collectPluginClassLoaders() {
|
||||
var pluginManager = Bukkit.getPluginManager();
|
||||
HashMap<java.lang.String, java.lang.ClassLoader> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message for when checking a compatibility condition fails.
|
||||
* @param problemFormat The format String to be used for explaining the problem. Requires one `%s` for the class name.
|
||||
* @param e The exception thrown by the check failure.
|
||||
*/
|
||||
private void logCompatibilityCheckFailureMessage(@Nonnull String problemFormat, @Nonnull Exception e) {
|
||||
NitoriUtil.getPreferredLogger().error(NitoriUtil.makeLogMessage(problemFormat.formatted("`" + this.getClass().getName() + "`") + "! I'll assume the related plugin(s) will otherwise hang and always run on main. Please update my intel! Exception follows:\n" +
|
||||
e));
|
||||
}
|
||||
|
||||
/**
|
||||
* What to do when the plugin classes are not found.
|
||||
* @param e The exception thrown by the plugin classes not being found.
|
||||
*/
|
||||
private void onPluginClassesNotFound(ReflectiveOperationException e) {
|
||||
logCompatibilityCheckFailureMessage("FAILED TO LOCATE CLASSES FOR %s", e);
|
||||
}
|
||||
|
||||
/**
|
||||
* What to do when attempting to use the plugin reflection references fails.
|
||||
* @param e The exception thrown by an attempt to use a plugin reflection reference fails.
|
||||
*/
|
||||
private void onReferenceUseFailure(CompletionException e) {
|
||||
logCompatibilityCheckFailureMessage("FAILED TO USE REFLECTED REFERENCES FOR %s!", e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and caches plugin reflection references.
|
||||
* @param pluginClassLoaders The plugin class loaders used to resolve the reflection references.
|
||||
* @throws ReflectiveOperationException If something went wrong finding the resolutions for reflection references.
|
||||
*/
|
||||
protected abstract void collectReferences(@Nonnull Map<String, ClassLoader> pluginClassLoaders) throws ReflectiveOperationException;
|
||||
|
||||
/**
|
||||
* Fullfills a plugin compatibility boolean condition.
|
||||
* @param conditionCallback The function used to ultimately determine if the condition passes or fails.
|
||||
* The condition callback may throw an {@link IllegalStateException} if the reflection references have not yet been resolved.
|
||||
* @return If the necessary plugins aren't loaded, false.
|
||||
* Otherwise, if the reflection references failed to be resolved, or an unexpected exception is thrown by the condition callback, true.
|
||||
* Otherwise, the result of the condition callback.
|
||||
* @throws IllegalStateException If the reflection references weren't resolved before calling the condition callback.
|
||||
*/
|
||||
boolean completePluginCompatibilityCondition(BooleanSupplier conditionCallback) {
|
||||
synchronized (this) { // compatibility classes are singletons so syncing on itself is safe
|
||||
if (!this.reflectionResolutionAttempted) {
|
||||
var pluginClassLoaders = collectPluginClassLoaders();
|
||||
if (pluginClassLoaders == null) {
|
||||
return false;
|
||||
}
|
||||
var thisClassName = this.getClass().getSimpleName();
|
||||
NitoriUtil.getPreferredLogger().info(NitoriUtil.makeLogMessage("Resolving reflection references for " + thisClassName + "."));
|
||||
try {
|
||||
collectReferences(pluginClassLoaders);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
onPluginClassesNotFound(e);
|
||||
return true;
|
||||
} finally {
|
||||
this.reflectionResolutionAttempted = true;
|
||||
}
|
||||
NitoriUtil.getPreferredLogger().info(NitoriUtil.makeLogMessage("Resolved reflection references for " + thisClassName + "."));
|
||||
}
|
||||
}
|
||||
try {
|
||||
return conditionCallback.getAsBoolean();
|
||||
} catch (IllegalStateException e) {
|
||||
NitoriUtil.getPreferredLogger().error(NitoriUtil.makeLogMessage("Hey, could you not forget to inform me on how to resolve the reflection references??"));
|
||||
throw e;
|
||||
} catch (CompletionException e) {
|
||||
onReferenceUseFailure(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
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<String, ClassLoader> 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility;
|
||||
|
||||
/**
|
||||
* Stores and provides access to all plugin compatibility instances.
|
||||
*/
|
||||
public class PluginCompatibilityRegistry {
|
||||
|
||||
/**
|
||||
* Instance for Citizens compatibility.
|
||||
*/
|
||||
public static final PluginCompatibilityCitizens CITIZENS = new PluginCompatibilityCitizens();
|
||||
|
||||
/**
|
||||
* Instance for Train Carts compatibility.
|
||||
*/
|
||||
public static final PluginCompatibilityTrainCarts TRAIN_CARTS = new PluginCompatibilityTrainCarts();
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
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<String, ClassLoader> 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);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility.reflection;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A resolver for a reflection reference to be invoked at runtime when the resolution should be valid.
|
||||
* @param <T> The data type to be returned.
|
||||
*/
|
||||
public abstract class BaseReflectionReferenceResolver<T> {
|
||||
/**
|
||||
* The resolution of this reflection reference. Is null until resolved.
|
||||
*/
|
||||
@Nullable
|
||||
private T resolution;
|
||||
|
||||
/**
|
||||
* Resolves this reflection reference.
|
||||
* @param classLoader The class loader to use for resolving this reflection reference
|
||||
* @return The resolution of this reflection reference.
|
||||
* @throws ReflectiveOperationException If something went wrong finding the resolution for this reflection reference
|
||||
*/
|
||||
@Nonnull
|
||||
protected abstract T resolve(ClassLoader classLoader) throws ReflectiveOperationException;
|
||||
|
||||
/**
|
||||
* Accept a classloader and resolve this reflection reference.
|
||||
* @param classLoader The class loader to use for resolving this reflection reference
|
||||
* @throws ReflectiveOperationException If something went wrong finding the resolution for this reflection reference
|
||||
*/
|
||||
public void accept(ClassLoader classLoader) throws ReflectiveOperationException {
|
||||
this.resolution = resolve(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolution of this reflection reference.
|
||||
* @return The resolution of this reflection reference.
|
||||
* @throws IllegalStateException If this reflection reference has not yet been resolved.
|
||||
*/
|
||||
@Nonnull
|
||||
public T getResolution() {
|
||||
if (this.resolution == null) {
|
||||
throw new IllegalStateException("Reflection reference has not been resolved!");
|
||||
}
|
||||
return this.resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common function for resolving a class.
|
||||
* @param classLoader The class loader to use for finding the class.
|
||||
* @param classPath The path of the class.
|
||||
* @return The class
|
||||
* @throws ClassNotFoundException If the class could not be found with the given class loader
|
||||
*/
|
||||
@Nonnull
|
||||
protected static Class<?> resolveClass(ClassLoader classLoader, String classPath) throws ClassNotFoundException {
|
||||
return Class.forName(classPath, false, classLoader);
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility.reflection;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClassReflectionReferenceResolver extends BaseReflectionReferenceResolver<Class<?>> {
|
||||
|
||||
private final String classPath;
|
||||
|
||||
/**
|
||||
* Creates a new class resolver.
|
||||
* @param classPath The path of the class.
|
||||
*/
|
||||
public ClassReflectionReferenceResolver(String classPath) {
|
||||
this.classPath = classPath;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected Class<?> resolve(ClassLoader classLoader) throws ClassNotFoundException {
|
||||
return resolveClass(classLoader, this.classPath);
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility.reflection;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class FieldReflectionReferenceResolver extends BaseReflectionReferenceResolver<Field> {
|
||||
|
||||
private final String classPath;
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
/**
|
||||
* Creates a new field resolver.
|
||||
* @param classPath The path of the class containing the field.
|
||||
* @param fieldName the name of the field.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.compatibility.reflection;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MethodReflectionReferenceResolver extends BaseReflectionReferenceResolver<Method> {
|
||||
|
||||
private final String classPath;
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final Class<?>[] methodParameterClasses;
|
||||
|
||||
/**
|
||||
* Creates a new method resolver.
|
||||
* @param classPath The path of the class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param methodParameterClasses The classes of the parameter types of the method, per parameter, in order.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ package net.gensokyoreimagined.nitori.config;
|
||||
//import org.simpleyaml.configuration.file.YamlFile;
|
||||
//import org.simpleyaml.exceptions.InvalidConfigurationException;
|
||||
|
||||
//TODO: everything lol
|
||||
public class NitoriConfig {
|
||||
|
||||
/*
|
||||
|
||||
@@ -22,9 +22,10 @@ import it.unimi.dsi.fastutil.objects.ReferenceSets;
|
||||
//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 +89,7 @@ public class ChunkMapMixin implements IMixinChunkMapAccess {
|
||||
@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 +137,7 @@ public class ChunkMapMixin implements IMixinChunkMapAccess {
|
||||
@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 +155,16 @@ public class ChunkMapMixin implements IMixinChunkMapAccess {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
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<SynchedEntityData.DataValue<?>> trackedDataValues;
|
||||
|
||||
@Shadow
|
||||
public void sendPairingData(ServerPlayer serverplayer, Consumer<Packet<ClientGamePacketListener>> consumer) {
|
||||
throw new AssertionError("Mixin failed to apply!");
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
package net.gensokyoreimagined.nitori.util;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Objects;
|
||||
|
||||
public class NitoriUtil {
|
||||
public static final String modName;
|
||||
|
||||
static {
|
||||
try {
|
||||
// if the Ignite mod config file is missing, there's a big problem
|
||||
modName = JsonParser.parseReader(new InputStreamReader(Objects.requireNonNull(NitoriUtil.class.getResource("/ignite.mod.json")).openStream())).getAsJsonObject().get("id").getAsString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Logger getPreferredLogger() {
|
||||
return LogUtils.getClassLogger();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String makeLogMessage(String baseMessage) {
|
||||
return "[" + modName + "] " + baseMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "Nitori",
|
||||
"version": "1.0.6",
|
||||
"version": "1.1-SNAPSHOT",
|
||||
"mixins": [
|
||||
"mixins.core.json"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user