Merge branch 'dev-multithreaded-tracker'

This commit is contained in:
Altiami
2024-05-05 09:42:05 -07:00
16 changed files with 564 additions and 79 deletions

View File

@@ -12,4 +12,4 @@ plugins {
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "GensouHacks"
rootProject.name = "Nitori"

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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)
);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
/*

View File

@@ -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);
}
}
}
}

View File

@@ -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!");

View File

@@ -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;
}
}

View File

@@ -1,6 +1,6 @@
{
"id": "Nitori",
"version": "1.0.6",
"version": "1.1-SNAPSHOT",
"mixins": [
"mixins.core.json"
]