initial 1.18 port
This commit is contained in:
parent
360eea8b1c
commit
7e2aaabd19
13
build.gradle
13
build.gradle
|
|
@ -4,7 +4,7 @@ plugins {
|
||||||
id 'com.matthewprenger.cursegradle' version '1.4.0'
|
id 'com.matthewprenger.cursegradle' version '1.4.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
group = 'org.embeddedt'
|
group = 'org.embeddedt'
|
||||||
version = '1.6.0-beta3'
|
version = '1.6.0-beta3'
|
||||||
|
|
@ -66,10 +66,10 @@ dependencies {
|
||||||
forge "net.minecraftforge:forge:${project.forge_version}"
|
forge "net.minecraftforge:forge:${project.forge_version}"
|
||||||
modRuntimeOnly "curse.maven:lazydfu-460819:${lazydfu_version}"
|
modRuntimeOnly "curse.maven:lazydfu-460819:${lazydfu_version}"
|
||||||
|
|
||||||
modCompileOnly("mezz.jei:jei-${minecraft_version}:${jei_version}")
|
|
||||||
modRuntimeOnly("mezz.jei:jei-${minecraft_version}:${jei_version}")
|
|
||||||
|
|
||||||
modCompileOnly("curse.maven:refinedstorage-243076:${refined_storage_version}")
|
modCompileOnly("curse.maven:refinedstorage-243076:${refined_storage_version}")
|
||||||
|
|
||||||
|
// compile against the JEI API but do not include it at runtime
|
||||||
|
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
|
|
@ -79,10 +79,7 @@ tasks.withType(JavaCompile) {
|
||||||
// If Javadoc is generated, this must be specified in that task too.
|
// If Javadoc is generated, this must be specified in that task too.
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
|
|
||||||
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
|
def targetVersion = 17
|
||||||
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
|
|
||||||
// We'll use that if it's available, but otherwise we'll use the older option.
|
|
||||||
def targetVersion = 8
|
|
||||||
if (JavaVersion.current().isJava9Compatible()) {
|
if (JavaVersion.current().isJava9Compatible()) {
|
||||||
options.release = targetVersion
|
options.release = targetVersion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@ org.gradle.jvmargs=-Xmx1G
|
||||||
loom.platform=forge
|
loom.platform=forge
|
||||||
|
|
||||||
mod_id=modernfix
|
mod_id=modernfix
|
||||||
minecraft_version=1.16.5
|
minecraft_version=1.18.2
|
||||||
forge_version=1.16.5-36.2.39
|
forge_version=1.18.2-40.2.1
|
||||||
lazydfu_version=3249059
|
lazydfu_version=3544496
|
||||||
mekanism_version=1.16.5-10.1.2.457
|
parchment_version=2022.11.06
|
||||||
parchment_version=2022.03.06
|
refined_storage_version=4392829
|
||||||
jei_version=7.7.1.153
|
jei_version=10.2.1.283
|
||||||
refined_storage_version=3807951
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
package cpw.mods.modlauncher;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
|
||||||
|
|
||||||
import cpw.mods.modlauncher.api.ITransformer;
|
|
||||||
import cpw.mods.modlauncher.api.ITransformerActivity;
|
|
||||||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
|
||||||
import net.minecraftforge.coremod.transformer.CoreModBaseTransformer;
|
|
||||||
import net.minecraftforge.fml.loading.FMLPaths;
|
|
||||||
import net.minecraftforge.fml.loading.LoadingModList;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.classloading.api.IHashableTransformer;
|
|
||||||
import org.embeddedt.modernfix.classloading.hashers.CoreModTransformerHasher;
|
|
||||||
import org.embeddedt.modernfix.classloading.hashers.MixinTransformerHasher;
|
|
||||||
import org.embeddedt.modernfix.util.FileUtil;
|
|
||||||
import org.objectweb.asm.Type;
|
|
||||||
import org.spongepowered.asm.launch.MixinLaunchPluginLegacy;
|
|
||||||
|
|
||||||
import javax.lang.model.SourceVersion;
|
|
||||||
|
|
||||||
public class ModernFixCachingClassTransformer extends ClassTransformer {
|
|
||||||
public static final Logger LOGGER = LogManager.getLogger("ModernFixCachingTransformer");
|
|
||||||
|
|
||||||
public static File CLASS_CACHE_FOLDER = null;
|
|
||||||
private final LaunchPluginHandler pluginHandler;
|
|
||||||
private final Map<String, ILaunchPluginService> plugins;
|
|
||||||
private final TransformStore transformStore;
|
|
||||||
private final TransformerAuditTrail auditTrail;
|
|
||||||
private final TransformingClassLoader transformingClassLoader;
|
|
||||||
private final HashMap<String, List<ITransformer<?>>> transformersByClass;
|
|
||||||
|
|
||||||
private ConcurrentHashMap<String, Pair<List<byte[]>, byte[]>> transformationCache;
|
|
||||||
private ForkJoinPool classSaverPool = ForkJoinPool.commonPool();
|
|
||||||
|
|
||||||
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
|
||||||
|
|
||||||
public static ThreadLocal<MessageDigest> systemHasher = ThreadLocal.withInitial(() -> {
|
|
||||||
try {
|
|
||||||
return MessageDigest.getInstance("SHA-256");
|
|
||||||
} catch(NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public ModernFixCachingClassTransformer(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoader transformingClassLoader, TransformerAuditTrail trail) {
|
|
||||||
super(transformStore, pluginHandler, transformingClassLoader, trail);
|
|
||||||
CLASS_CACHE_FOLDER = FileUtil.childFile(FMLPaths.GAMEDIR.get().resolve("modernfix").resolve("classCacheV1").toFile());
|
|
||||||
this.transformStore = transformStore;
|
|
||||||
this.pluginHandler = pluginHandler;
|
|
||||||
this.transformingClassLoader = transformingClassLoader;
|
|
||||||
this.auditTrail = trail;
|
|
||||||
/* Build a lookup table of all transformers for a given class */
|
|
||||||
this.transformersByClass = new HashMap<>();
|
|
||||||
try {
|
|
||||||
Field pluginsField = LaunchPluginHandler.class.getDeclaredField("plugins");
|
|
||||||
pluginsField.setAccessible(true);
|
|
||||||
this.plugins = (Map<String, ILaunchPluginService>)pluginsField.get(this.pluginHandler);
|
|
||||||
Field transformersByTypeField = TransformStore.class.getDeclaredField("transformers");
|
|
||||||
transformersByTypeField.setAccessible(true);
|
|
||||||
Field transformersMapField = TransformList.class.getDeclaredField("transformers");
|
|
||||||
transformersMapField.setAccessible(true);
|
|
||||||
EnumMap<TransformTargetLabel.LabelType, TransformList<?>> transformersByType = (EnumMap<TransformTargetLabel.LabelType, TransformList<?>>)transformersByTypeField.get(this.transformStore);
|
|
||||||
for(TransformList<?> transformList : transformersByType.values()) {
|
|
||||||
Map<TransformTargetLabel, List<ITransformer<?>>> transformers = (Map<TransformTargetLabel, List<ITransformer<?>>>)transformersMapField.get(transformList);
|
|
||||||
for(Map.Entry<TransformTargetLabel, List<ITransformer<?>>> entry : transformers.entrySet()) {
|
|
||||||
String className = entry.getKey().getClassName().getClassName();
|
|
||||||
List<ITransformer<?>> transformerList = this.transformersByClass.computeIfAbsent(className, k -> new ArrayList<>());
|
|
||||||
transformerList.addAll(entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(List<ITransformer<?>> transformerList : this.transformersByClass.values()) {
|
|
||||||
transformerList.sort((t1, t2) -> Comparator.<String>naturalOrder().compare(StringUtils.join(t1.labels(), " "), StringUtils.join(t2.labels(), " ")));
|
|
||||||
}
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<byte[]> computeHash(String className, byte[] inputClass, String reason) {
|
|
||||||
final String internalName = className.replace('.', '/');
|
|
||||||
final Type classDesc = Type.getObjectType(internalName);
|
|
||||||
ArrayList<ILaunchPluginService> pluginList = new ArrayList<>();
|
|
||||||
for(ILaunchPluginService plugin : plugins.values()) {
|
|
||||||
if(!plugin.handlesClass(classDesc, inputClass.length == 0, reason).isEmpty()) {
|
|
||||||
pluginList.add(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final boolean needsTransforming = transformStore.needsTransforming(internalName);
|
|
||||||
if (!needsTransforming && pluginList.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/* Now compute the hash list for the required transformers */
|
|
||||||
ArrayList<byte[]> hashList = new ArrayList<>();
|
|
||||||
pluginList.sort((service1, service2) -> Comparator.<String>naturalOrder().compare(service1.name(), service2.name()));
|
|
||||||
for(ILaunchPluginService service : pluginList) {
|
|
||||||
byte[] hash = obtainHash(service, className);
|
|
||||||
if(hash == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
hashList.add(hash);
|
|
||||||
}
|
|
||||||
if(needsTransforming) {
|
|
||||||
List<ITransformer<?>> transformers = this.transformersByClass.get(internalName);
|
|
||||||
if(transformers != null) {
|
|
||||||
for(ITransformer<?> transformer : transformers) {
|
|
||||||
byte[] hash = obtainHash(transformer, className);
|
|
||||||
if(hash == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
hashList.add(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Hash the class itself last, so that we bail out early if plugins can't hash */
|
|
||||||
MessageDigest hasher = systemHasher.get();
|
|
||||||
hasher.reset();
|
|
||||||
hashList.add(hasher.digest(inputClass));
|
|
||||||
return hashList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the hashed list of transformers and use a cached version of the class if possible. This code needs
|
|
||||||
* to be very fast as the entire point is to spend very little time doing transformation work that was done before.
|
|
||||||
* @param inputClass The bytecode to be transformed
|
|
||||||
* @param className Name of the class
|
|
||||||
* @param reason Reason for the class being loaded
|
|
||||||
* @return The transformed version of the class
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public byte[] transform(byte[] inputClass, String className, String reason) {
|
|
||||||
/* We only want to cache actual transformations */
|
|
||||||
if(ITransformerActivity.CLASSLOADING_REASON.equals(reason) || "mixin".equals(reason)) {
|
|
||||||
final byte[] classToHash = inputClass;
|
|
||||||
ArrayList<byte[]> hashList = computeHash(className, classToHash, reason);
|
|
||||||
if(hashList == null)
|
|
||||||
return super.transform(inputClass, className, reason);
|
|
||||||
/* Check if the cache contains a transformed class matching these hashes */
|
|
||||||
/* TODO maybe sanitize the class name? */
|
|
||||||
File cacheLocation = new File(CLASS_CACHE_FOLDER, className.replace('.', '/') + "." + reason);
|
|
||||||
boolean hashesMatch = true;
|
|
||||||
try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(cacheLocation))) {
|
|
||||||
ArrayList<byte[]> savedHash = (ArrayList<byte[]>)stream.readObject();
|
|
||||||
byte[] savedInputClass = (byte[])stream.readObject();
|
|
||||||
if(hashList != null) {
|
|
||||||
for(int i = 0; i < savedHash.size(); i++) {
|
|
||||||
if(!Arrays.equals(savedHash.get(i), hashList.get(i))) {
|
|
||||||
hashesMatch = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
hashesMatch = false;
|
|
||||||
if(hashesMatch)
|
|
||||||
inputClass = savedInputClass;
|
|
||||||
} catch(IOException | ClassNotFoundException | ClassCastException e) {
|
|
||||||
if(!(e instanceof FileNotFoundException))
|
|
||||||
e.printStackTrace();
|
|
||||||
hashesMatch = false;
|
|
||||||
}
|
|
||||||
if(!hashesMatch) {
|
|
||||||
inputClass = super.transform(inputClass, className, reason);
|
|
||||||
if(hashList != null) {
|
|
||||||
final byte[] classToSave = inputClass;
|
|
||||||
final ArrayList<byte[]> hashListToSave = hashList;
|
|
||||||
classSaverPool.submit(() -> {
|
|
||||||
cacheLocation.getParentFile().mkdirs();
|
|
||||||
try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(cacheLocation))) {
|
|
||||||
stream.writeObject(hashListToSave);
|
|
||||||
stream.writeObject(classToSave);
|
|
||||||
} catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return inputClass;
|
|
||||||
}
|
|
||||||
return super.transform(inputClass, className, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final byte[] FORGE_HASH = LoadingModList.get().getModFileById("forge").getMods().get(0).getVersion().toString().getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
private byte[] obtainHash(Object o, String className) {
|
|
||||||
if(o instanceof CoreModBaseTransformer) {
|
|
||||||
return CoreModTransformerHasher.obtainHash((CoreModBaseTransformer<?>)o);
|
|
||||||
} else if(o instanceof MixinLaunchPluginLegacy) {
|
|
||||||
return MixinTransformerHasher.obtainHash((MixinLaunchPluginLegacy)o, className);
|
|
||||||
} else if(o instanceof IHashableTransformer) {
|
|
||||||
return ((IHashableTransformer)o).getHashForClass(className);
|
|
||||||
} else if(o.getClass().getName().startsWith("net.minecraftforge.")) {
|
|
||||||
return FORGE_HASH;
|
|
||||||
} else {
|
|
||||||
LOGGER.warn("No hash implementation found for: " + o.getClass().getName());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,21 +2,19 @@ package org.embeddedt.modernfix;
|
||||||
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.DistExecutor;
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
import net.minecraftforge.fml.ExtensionPoint;
|
import net.minecraftforge.fml.IExtensionPoint;
|
||||||
import net.minecraftforge.fml.ModLoadingContext;
|
import net.minecraftforge.fml.ModLoadingContext;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.fml.config.ModConfig;
|
import net.minecraftforge.fml.config.ModConfig;
|
||||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
|
||||||
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
|
||||||
import net.minecraftforge.fml.loading.FMLLoader;
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
import net.minecraftforge.fml.network.FMLNetworkConstants;
|
import net.minecraftforge.network.NetworkConstants;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
|
||||||
|
|
@ -40,12 +38,12 @@ public class ModernFix {
|
||||||
// Register ourselves for server and other game events we are interested in
|
// Register ourselves for server and other game events we are interested in
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
MinecraftForge.EVENT_BUS.register(this);
|
||||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient()));
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient()));
|
||||||
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onServerStarted(FMLServerStartedEvent event) {
|
public void onServerStarted(ServerStartedEvent event) {
|
||||||
if(FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
|
if(FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
|
||||||
float gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
float gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||||
ModernFix.LOGGER.warn("Dedicated server took " + gameStartTime + " seconds to load");
|
ModernFix.LOGGER.warn("Dedicated server took " + gameStartTime + " seconds to load");
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package org.embeddedt.modernfix;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||||
import net.minecraft.client.gui.screens.TitleScreen;
|
import net.minecraft.client.gui.screens.TitleScreen;
|
||||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
import net.minecraftforge.client.event.ScreenOpenEvent;
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
import net.minecraftforge.eventbus.api.EventPriority;
|
import net.minecraftforge.eventbus.api.EventPriority;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
|
@ -23,10 +23,10 @@ public class ModernFixClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||||
public void onMultiplayerConnect(GuiOpenEvent event) {
|
public void onMultiplayerConnect(ScreenOpenEvent event) {
|
||||||
if(event.getGui() instanceof ConnectScreen && !event.isCanceled()) {
|
if(event.getScreen() instanceof ConnectScreen && !event.isCanceled()) {
|
||||||
worldLoadStartTime = System.nanoTime();
|
worldLoadStartTime = System.nanoTime();
|
||||||
} else if (event.getGui() instanceof TitleScreen && gameStartTimeSeconds < 0) {
|
} else if (event.getScreen() instanceof TitleScreen && gameStartTimeSeconds < 0) {
|
||||||
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
package org.embeddedt.modernfix.classloading;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
|
||||||
import net.minecraftforge.fml.loading.FMLLoader;
|
|
||||||
import net.minecraftforge.fml.loading.LoadingModList;
|
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.*;
|
|
||||||
import net.minecraftforge.forgespi.language.IModInfo;
|
|
||||||
import net.minecraftforge.forgespi.locating.IModFile;
|
|
||||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class ModernFixResourceFinder {
|
|
||||||
private static HashMap<String, List<URL>> urlsForClass = null;
|
|
||||||
private static final Class<? extends IModLocator> MINECRAFT_LOCATOR;
|
|
||||||
private static Field explodedDirModsField = null;
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("ModernFixResourceFinder");
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
MINECRAFT_LOCATOR = (Class<? extends IModLocator>)Class.forName("net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer$MinecraftLocator");
|
|
||||||
} catch(ClassNotFoundException e) {
|
|
||||||
/* that shouldn't happen */
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static synchronized void init() throws ReflectiveOperationException {
|
|
||||||
urlsForClass = new HashMap<>();
|
|
||||||
LOGGER.info("Start building list of class locations...");
|
|
||||||
for(ModFileInfo fileInfo : LoadingModList.get().getModFiles()) {
|
|
||||||
ModFile file = fileInfo.getFile();
|
|
||||||
IModLocator locator = file.getLocator();
|
|
||||||
Iterable<Path> rootPath = getRootPathForLocator(locator, file);
|
|
||||||
for(Path root : rootPath) {
|
|
||||||
try(Stream<Path> stream = Files.walk(root)) {
|
|
||||||
stream
|
|
||||||
.map(root::relativize)
|
|
||||||
.forEach(path -> {
|
|
||||||
String strPath = path.toString();
|
|
||||||
URL url = (URL)LamdbaExceptionUtils.uncheck(() -> {
|
|
||||||
return new URL("modjar://" + fileInfo.getMods().get(0).getModId() + "/" + strPath);
|
|
||||||
});
|
|
||||||
List<URL> urlList = urlsForClass.get(strPath);
|
|
||||||
if(urlList != null) {
|
|
||||||
if(urlList.size() > 1)
|
|
||||||
urlList.add(url);
|
|
||||||
else {
|
|
||||||
/* Convert singleton to real list */
|
|
||||||
ArrayList<URL> newList = new ArrayList<>(urlList);
|
|
||||||
newList.add(url);
|
|
||||||
urlsForClass.put(strPath, newList);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Use a singleton list initially to keep memory usage down */
|
|
||||||
urlsForClass.put(strPath, Collections.singletonList(url));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(List<URL> list : urlsForClass.values()) {
|
|
||||||
if(list instanceof ArrayList)
|
|
||||||
((ArrayList<URL>)list).trimToSize();
|
|
||||||
}
|
|
||||||
LOGGER.info("Finish building");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Iterable<Path> getRootPathForLocator(IModLocator locator, ModFile file) throws ReflectiveOperationException {
|
|
||||||
if(locator instanceof AbstractJarFileLocator) {
|
|
||||||
FileSystem modFs = locator.findPath(file, ".").getFileSystem();
|
|
||||||
return modFs.getRootDirectories();
|
|
||||||
} else if (locator instanceof ExplodedDirectoryLocator) {
|
|
||||||
if(explodedDirModsField == null) {
|
|
||||||
explodedDirModsField = ExplodedDirectoryLocator.class.getDeclaredField("mods");
|
|
||||||
explodedDirModsField.setAccessible(true);
|
|
||||||
}
|
|
||||||
Map<IModFile, Pair<Path, List<Path>>> mods = (Map<IModFile, Pair<Path, List<Path>>>)explodedDirModsField.get(locator);
|
|
||||||
return mods.get(file).getRight();
|
|
||||||
} else if(MINECRAFT_LOCATOR.isAssignableFrom(locator.getClass())) {
|
|
||||||
Path mcJar = FMLLoader.getMCPaths()[0];
|
|
||||||
if(Files.isDirectory(mcJar)) {
|
|
||||||
return mcJar;
|
|
||||||
} else {
|
|
||||||
return locator.findPath(file, ".").getFileSystem().getRootDirectories();
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw new UnsupportedOperationException("Unknown ModLocator type: " + locator.getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Pattern SLASH_REPLACER = Pattern.compile("/+");
|
|
||||||
|
|
||||||
public static Enumeration<URL> findAllURLsForResource(String input) {
|
|
||||||
input = SLASH_REPLACER.matcher(input).replaceAll("/");
|
|
||||||
List<URL> urlList = urlsForClass.get(input);
|
|
||||||
if(urlList != null)
|
|
||||||
return Collections.enumeration(urlList);
|
|
||||||
else {
|
|
||||||
return Collections.emptyEnumeration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package org.embeddedt.modernfix.classloading.api;
|
|
||||||
|
|
||||||
public interface IHashableTransformer {
|
|
||||||
/**
|
|
||||||
* Called on an ILaunchPluginService or ITransformer to obtain a unique hash of the transformations that will be applied.
|
|
||||||
* Used to invalidate the transformation cache when needed.
|
|
||||||
* @param className Name of class being transformed
|
|
||||||
* @return A unique hash of the transformations that will be applied
|
|
||||||
*/
|
|
||||||
byte[] getHashForClass(String className);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
package org.embeddedt.modernfix.classloading.hashers;
|
|
||||||
|
|
||||||
import cpw.mods.modlauncher.ModernFixCachingClassTransformer;
|
|
||||||
import net.minecraftforge.coremod.CoreMod;
|
|
||||||
import net.minecraftforge.coremod.transformer.CoreModBaseTransformer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class CoreModTransformerHasher {
|
|
||||||
private static final ConcurrentHashMap<CoreMod, byte[]> hashForCoremod;
|
|
||||||
private static Field coremodField;
|
|
||||||
|
|
||||||
static {
|
|
||||||
hashForCoremod = new ConcurrentHashMap<>();
|
|
||||||
try {
|
|
||||||
coremodField = CoreModBaseTransformer.class.getDeclaredField("coreMod");
|
|
||||||
coremodField.setAccessible(true);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] hashCoreMod(CoreMod coreMod) {
|
|
||||||
byte[] coreModContents;
|
|
||||||
try {
|
|
||||||
coreModContents = Files.readAllBytes(coreMod.getPath());
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
MessageDigest hasher = ModernFixCachingClassTransformer.systemHasher.get();
|
|
||||||
hasher.reset();
|
|
||||||
return hasher.digest(coreModContents);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] obtainHash(CoreModBaseTransformer<?> transformer) {
|
|
||||||
CoreMod coremod;
|
|
||||||
try {
|
|
||||||
coremod = (CoreMod)coremodField.get(transformer);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return hashForCoremod.computeIfAbsent(coremod, CoreModTransformerHasher::hashCoreMod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
package org.embeddedt.modernfix.classloading.hashers;
|
|
||||||
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import cpw.mods.modlauncher.ModernFixCachingClassTransformer;
|
|
||||||
import org.spongepowered.asm.launch.IClassProcessor;
|
|
||||||
import org.spongepowered.asm.launch.MixinLaunchPluginLegacy;
|
|
||||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.invoke.arg.ArgsClassGenerator;
|
|
||||||
import org.spongepowered.asm.mixin.transformer.ext.Extensions;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class MixinTransformerHasher {
|
|
||||||
private static HashMap<String, byte[]> hashesByClass = null;
|
|
||||||
private final static MessageDigest hasher;
|
|
||||||
|
|
||||||
private static Field processorsListField, transformerField, processorField, environmentField;
|
|
||||||
|
|
||||||
private static boolean fixedArgsClassCount = false;
|
|
||||||
|
|
||||||
private static final byte[] NO_MIXINS = new byte[] {(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef};
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
hasher = MessageDigest.getInstance("SHA-256");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] obtainHash(MixinLaunchPluginLegacy plugin, String className) {
|
|
||||||
/* FIXME runs too early right now, and therefore doesn't pick up the list of mixins correctly */
|
|
||||||
synchronized (MixinTransformerHasher.class) {
|
|
||||||
if(hashesByClass == null) {
|
|
||||||
try {
|
|
||||||
if(processorsListField == null) {
|
|
||||||
processorsListField = MixinLaunchPluginLegacy.class.getDeclaredField("processors");
|
|
||||||
processorsListField.setAccessible(true);
|
|
||||||
}
|
|
||||||
List<IClassProcessor> processors = (List<IClassProcessor>) processorsListField.get(plugin);
|
|
||||||
Object transformHandler = null;
|
|
||||||
for(IClassProcessor processor : processors) {
|
|
||||||
if(processor.getClass().getName().equals("org.spongepowered.asm.service.modlauncher.MixinTransformationHandler")) {
|
|
||||||
transformHandler = processor;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(transformHandler == null)
|
|
||||||
throw new IllegalStateException("Mixin transform handler not found");
|
|
||||||
if(transformerField == null) {
|
|
||||||
transformerField = transformHandler.getClass().getDeclaredField("transformer");
|
|
||||||
transformerField.setAccessible(true);
|
|
||||||
}
|
|
||||||
Object transformer = transformerField.get(transformHandler);
|
|
||||||
if(!fixedArgsClassCount) {
|
|
||||||
Path syntheticFolderPath = ModernFixCachingClassTransformer.CLASS_CACHE_FOLDER.toPath().resolve("org").resolve("spongepowered").resolve("asm").resolve("synthetic");
|
|
||||||
if(Files.exists(syntheticFolderPath)) {
|
|
||||||
Field extensionsField = transformer.getClass().getDeclaredField("extensions");
|
|
||||||
extensionsField.setAccessible(true);
|
|
||||||
Extensions extensions = (Extensions)extensionsField.get(transformer);
|
|
||||||
ArgsClassGenerator argsGen = extensions.getGenerator(ArgsClassGenerator.class);
|
|
||||||
Field nextIndexField = ArgsClassGenerator.class.getDeclaredField("nextIndex");
|
|
||||||
try(Stream<Path> argsStream = Files.find(syntheticFolderPath, 1, (path, attr) -> path.getFileName().toString().startsWith("Args$"))) {
|
|
||||||
int[] startIndex = new int[1];
|
|
||||||
startIndex[0] = 1;
|
|
||||||
argsStream.forEach(path -> {
|
|
||||||
String fileName = path.getFileName().toString();
|
|
||||||
try {
|
|
||||||
int idx = Integer.parseInt(fileName.replace("Args$", ""));
|
|
||||||
startIndex[0] = Math.max(startIndex[0], idx + 1);
|
|
||||||
} catch(NumberFormatException e) {
|
|
||||||
ModernFixCachingClassTransformer.LOGGER.warn("Unexpected classname: " + fileName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nextIndexField.setAccessible(true);
|
|
||||||
nextIndexField.set(argsGen, startIndex[0]);
|
|
||||||
ModernFixCachingClassTransformer.LOGGER.debug("Patched ArgsClassGenerator to start at index " + startIndex[0]);
|
|
||||||
} catch(IOException e) {
|
|
||||||
ModernFixCachingClassTransformer.LOGGER.error("Failed to adjust Mixin synthetic args");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fixedArgsClassCount = true;
|
|
||||||
}
|
|
||||||
if(processorField == null) {
|
|
||||||
processorField = transformer.getClass().getDeclaredField("processor");
|
|
||||||
processorField.setAccessible(true);
|
|
||||||
}
|
|
||||||
Object processor = processorField.get(transformer);
|
|
||||||
if(environmentField == null) {
|
|
||||||
environmentField = processor.getClass().getDeclaredField("currentEnvironment");
|
|
||||||
environmentField.setAccessible(true);
|
|
||||||
}
|
|
||||||
MixinEnvironment currentEnv = (MixinEnvironment)environmentField.get(processor);
|
|
||||||
if(currentEnv == null || currentEnv.getPhase() != MixinEnvironment.Phase.DEFAULT) {
|
|
||||||
return null; /* no hash obtained until mixin is ready */
|
|
||||||
}
|
|
||||||
Field configsField = processor.getClass().getDeclaredField("configs");
|
|
||||||
configsField.setAccessible(true);
|
|
||||||
List<?> configs = (List<?>)configsField.get(processor);
|
|
||||||
Field mixinsField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinConfig").getDeclaredField("mixins");
|
|
||||||
mixinsField.setAccessible(true);
|
|
||||||
/* getTargetClasses can't be used because it's package-private */
|
|
||||||
Field classNamesField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo").getDeclaredField("targetClassNames");
|
|
||||||
classNamesField.setAccessible(true);
|
|
||||||
HashMap<String, ArrayList<IMixinInfo>> mixinsByClass = new HashMap<>();
|
|
||||||
for(Object config : configs) {
|
|
||||||
List<? extends IMixinInfo> mixins = (List<? extends IMixinInfo>)mixinsField.get(config);
|
|
||||||
for(IMixinInfo mixin : mixins) {
|
|
||||||
List<String> targetClassNames = (List<String>)classNamesField.get(mixin);
|
|
||||||
for(String s : targetClassNames) {
|
|
||||||
mixinsByClass.computeIfAbsent(s, k -> new ArrayList<>()).add(mixin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(ArrayList<IMixinInfo> infos : mixinsByClass.values()) {
|
|
||||||
infos.sort((info1, info2) -> Comparator.<String>naturalOrder().compare(info1.getClassName(), info2.getClassName()));
|
|
||||||
}
|
|
||||||
/* Now go through each class name and hash it */
|
|
||||||
HashMap<String, byte[]> hashesByClassInit = new HashMap<>();
|
|
||||||
for(Map.Entry<String, ArrayList<IMixinInfo>> mixinsForClass : mixinsByClass.entrySet()) {
|
|
||||||
hasher.reset();
|
|
||||||
for(IMixinInfo mixin : mixinsForClass.getValue()) {
|
|
||||||
URL url = Thread.currentThread().getContextClassLoader().getResource(mixin.getClassName().replace('.', '/') + ".class");
|
|
||||||
if(url == null)
|
|
||||||
throw new IllegalStateException("Can't find " + mixin.getClassName());
|
|
||||||
byte[] bytecode;
|
|
||||||
try {
|
|
||||||
bytecode = Resources.asByteSource(url).read();
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
hasher.update(bytecode);
|
|
||||||
}
|
|
||||||
hashesByClassInit.put(mixinsForClass.getKey().replace('/', '.'), hasher.digest());
|
|
||||||
}
|
|
||||||
hashesByClass = hashesByClassInit;
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hashesByClass.getOrDefault(className, NO_MIXINS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
package org.embeddedt.modernfix.core;
|
package org.embeddedt.modernfix.core;
|
||||||
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import cpw.mods.modlauncher.*;
|
|
||||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.embeddedt.modernfix.classloading.ModernFixResourceFinder;
|
|
||||||
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
||||||
import org.embeddedt.modernfix.core.config.Option;
|
import org.embeddedt.modernfix.core.config.Option;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
@ -14,14 +9,7 @@ import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||||
private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin.";
|
private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin.";
|
||||||
|
|
@ -38,88 +26,11 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||||
|
|
||||||
this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found",
|
this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found",
|
||||||
config.getOptionCount(), config.getOptionOverrideCount());
|
config.getOptionCount(), config.getOptionOverrideCount());
|
||||||
|
|
||||||
/* We abuse the constructor of a mixin plugin as a safe location to start modifying the classloader */
|
|
||||||
/* Swap the transformer for ours */
|
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
if(!(loader instanceof TransformingClassLoader)) {
|
|
||||||
throw new IllegalStateException("Expected a TransformingClassLoader");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if(isOptionEnabled("launch.class_search_cache.ModernFixResourceFinder")) {
|
|
||||||
Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder");
|
|
||||||
/* Construct a new list of resource finders, using similar logic to ML */
|
|
||||||
resourceFinderField.setAccessible(true);
|
|
||||||
Function<String, Enumeration<URL>> resourceFinder = constructResourceFinder();
|
|
||||||
/* Merge with the findResources implementation provided by the DelegatedClassLoader */
|
|
||||||
Field dclField = TransformingClassLoader.class.getDeclaredField("delegatedClassLoader");
|
|
||||||
dclField.setAccessible(true);
|
|
||||||
URLClassLoader dcl = (URLClassLoader)dclField.get(loader);
|
|
||||||
resourceFinder = EnumerationHelper.mergeFunctors(resourceFinder, LamdbaExceptionUtils.rethrowFunction(dcl::findResources));
|
|
||||||
resourceFinderField.set(loader, resourceFinder);
|
|
||||||
}
|
|
||||||
} catch(RuntimeException | ReflectiveOperationException e) {
|
|
||||||
logger.error("Failed to make classloading changes", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method defineClassMethod = null;
|
|
||||||
|
|
||||||
private Class<?> injectClassIntoSystemLoader(String className) throws ReflectiveOperationException, IOException {
|
|
||||||
ClassLoader systemLoader = ClassTransformer.class.getClassLoader();
|
|
||||||
if(defineClassMethod == null) {
|
|
||||||
defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
|
|
||||||
defineClassMethod.setAccessible(true);
|
|
||||||
}
|
|
||||||
byte[] newTransformerBytes = Resources.toByteArray(ModernFixMixinPlugin.class.getResource("/" + className.replace('.', '/') + ".class"));
|
|
||||||
return (Class<?>)defineClassMethod.invoke(systemLoader, className, newTransformerBytes, 0, newTransformerBytes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Function<String, Enumeration<URL>> constructResourceFinder() throws ReflectiveOperationException {
|
|
||||||
ModernFixResourceFinder.init();
|
|
||||||
Field servicesHandlerField = Launcher.class.getDeclaredField("transformationServicesHandler");
|
|
||||||
servicesHandlerField.setAccessible(true);
|
|
||||||
Object servicesHandler = servicesHandlerField.get(Launcher.INSTANCE);
|
|
||||||
Field serviceLookupField = servicesHandler.getClass().getDeclaredField("serviceLookup");
|
|
||||||
serviceLookupField.setAccessible(true);
|
|
||||||
Map<String, TransformationServiceDecorator> serviceLookup = (Map<String, TransformationServiceDecorator>)serviceLookupField.get(servicesHandler);
|
|
||||||
Method getClassLoaderMethod = TransformationServiceDecorator.class.getDeclaredMethod("getClassLoader");
|
|
||||||
getClassLoaderMethod.setAccessible(true);
|
|
||||||
Function<String, Enumeration<URL>> resourceEnumeratorLocator = ModernFixResourceFinder::findAllURLsForResource;
|
|
||||||
for(TransformationServiceDecorator decorator : serviceLookup.values()) {
|
|
||||||
Function<String, Optional<URL>> func = (Function<String, Optional<URL>>)getClassLoaderMethod.invoke(decorator);
|
|
||||||
if(func != null) {
|
|
||||||
resourceEnumeratorLocator = EnumerationHelper.mergeFunctors(resourceEnumeratorLocator, EnumerationHelper.fromOptional(func));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resourceEnumeratorLocator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad(String mixinPackage) {
|
public void onLoad(String s) {
|
||||||
try {
|
|
||||||
if(isOptionEnabled("launch.transformer_cache.ModernFixClassTransformer")) {
|
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
Field classTransformerField = TransformingClassLoader.class.getDeclaredField("classTransformer");
|
|
||||||
classTransformerField.setAccessible(true);
|
|
||||||
ClassTransformer t = (ClassTransformer)classTransformerField.get(loader);
|
|
||||||
TransformStore store = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "transformers");
|
|
||||||
LaunchPluginHandler pluginHandler = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "pluginHandler");
|
|
||||||
TransformerAuditTrail trail = ObfuscationReflectionHelper.getPrivateValue(ClassTransformer.class, t, "auditTrail");
|
|
||||||
injectClassIntoSystemLoader("org.embeddedt.modernfix.util.FileUtil");
|
|
||||||
injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.api.IHashableTransformer");
|
|
||||||
injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.hashers.CoreModTransformerHasher");
|
|
||||||
injectClassIntoSystemLoader("org.embeddedt.modernfix.classloading.hashers.MixinTransformerHasher");
|
|
||||||
Class<?> newTransformerClass = injectClassIntoSystemLoader("cpw.mods.modlauncher.ModernFixCachingClassTransformer");
|
|
||||||
Constructor<?> constructor = newTransformerClass.getConstructor(TransformStore.class, LaunchPluginHandler.class, TransformingClassLoader.class, TransformerAuditTrail.class);
|
|
||||||
ClassTransformer newTransformer = (ClassTransformer)constructor.newInstance(store, pluginHandler, loader, trail);
|
|
||||||
classTransformerField.set(loader, newTransformer);
|
|
||||||
|
|
||||||
logger.info("Successfully injected caching transformer");
|
|
||||||
}
|
|
||||||
} catch(RuntimeException | ReflectiveOperationException | IOException e) {
|
|
||||||
logger.error("Failed to make classloading changes", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import net.minecraftforge.common.ForgeConfigSpec;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.fml.config.ModConfig;
|
import net.minecraftforge.fml.config.ModConfig;
|
||||||
|
import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -52,7 +53,7 @@ public class ModernFixConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onModConfigEvent(final ModConfig.ModConfigEvent configEvent) {
|
public static void onModConfigEvent(final ModConfigEvent configEvent) {
|
||||||
if (configEvent.getConfig().getSpec() == COMMON_CONFIG) {
|
if (configEvent.getConfig().getSpec() == COMMON_CONFIG) {
|
||||||
bakeConfig();
|
bakeConfig();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,30 +20,20 @@ public class ModernFixEarlyConfig {
|
||||||
this.addMixinRule("feature.measure_time", true);
|
this.addMixinRule("feature.measure_time", true);
|
||||||
this.addMixinRule("feature.reduce_loading_screen_freezes", false);
|
this.addMixinRule("feature.reduce_loading_screen_freezes", false);
|
||||||
this.addMixinRule("perf.remove_biome_temperature_cache", true);
|
this.addMixinRule("perf.remove_biome_temperature_cache", true);
|
||||||
this.addMixinRule("perf.resourcepacks", true);
|
|
||||||
this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true);
|
this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true);
|
||||||
this.addMixinRule("perf.boost_worker_count", true);
|
|
||||||
this.addMixinRule("perf.skip_first_datapack_reload", true);
|
|
||||||
this.addMixinRule("perf.parallelize_model_loading", true);
|
this.addMixinRule("perf.parallelize_model_loading", true);
|
||||||
this.addMixinRule("perf.parallelize_model_loading.multipart", false);
|
this.addMixinRule("perf.parallelize_model_loading.multipart", false);
|
||||||
this.addMixinRule("bugfix.concurrency", true);
|
this.addMixinRule("bugfix.concurrency", true);
|
||||||
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
|
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
|
||||||
this.addMixinRule("bugfix.packet_leak", false);
|
|
||||||
this.addMixinRule("bugfix.structure_manager_crash", true);
|
|
||||||
this.addMixinRule("perf.async_jei", true);
|
|
||||||
this.addMixinRule("perf.thread_priorities", true);
|
this.addMixinRule("perf.thread_priorities", true);
|
||||||
this.addMixinRule("perf.preload_block_classes", false);
|
|
||||||
this.addMixinRule("perf.sync_executor_sleep", true);
|
this.addMixinRule("perf.sync_executor_sleep", true);
|
||||||
this.addMixinRule("perf.scan_cache", true);
|
this.addMixinRule("perf.scan_cache", true);
|
||||||
this.addMixinRule("perf.compress_biome_container", true);
|
|
||||||
this.addMixinRule("perf.nuke_empty_chunk_sections", true);
|
|
||||||
this.addMixinRule("perf.flatten_model_predicates", true);
|
this.addMixinRule("perf.flatten_model_predicates", true);
|
||||||
this.addMixinRule("perf.deduplicate_location", false);
|
this.addMixinRule("perf.deduplicate_location", false);
|
||||||
this.addMixinRule("perf.cache_blockstate_cache_arrays", true);
|
this.addMixinRule("perf.cache_blockstate_cache_arrays", true);
|
||||||
this.addMixinRule("perf.faster_baking", true);
|
this.addMixinRule("perf.faster_baking", true);
|
||||||
this.addMixinRule("perf.cache_model_materials", true);
|
this.addMixinRule("perf.cache_model_materials", true);
|
||||||
this.addMixinRule("perf.datapack_reload_exceptions", true);
|
this.addMixinRule("perf.datapack_reload_exceptions", true);
|
||||||
this.addMixinRule("perf.async_locator", true);
|
|
||||||
/* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */
|
/* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */
|
||||||
this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null);
|
this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null);
|
||||||
this.addMixinRule("safety", true);
|
this.addMixinRule("safety", true);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package org.embeddedt.modernfix.jei.async;
|
|
||||||
|
|
||||||
public interface IAsyncJeiStarter {
|
|
||||||
static void checkForLoadInterruption() {
|
|
||||||
if(((JEIReloadThread)Thread.currentThread()).isStopRequested())
|
|
||||||
throw new JEILoadingInterruptedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
package org.embeddedt.modernfix.jei.async;
|
|
||||||
|
|
||||||
public class JEILoadingInterruptedException extends Error {
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package org.embeddedt.modernfix.jei.async;
|
|
||||||
|
|
||||||
public class JEIReloadThread extends Thread {
|
|
||||||
private volatile boolean stopRequested;
|
|
||||||
|
|
||||||
public JEIReloadThread(Runnable runnable, String s) {
|
|
||||||
super(runnable, s);
|
|
||||||
this.stopRequested = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestStop() {
|
|
||||||
stopRequested = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStopRequested() {
|
|
||||||
return stopRequested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.bugfix.concurrency;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
@Mixin(RenderType.CompositeRenderType.class)
|
|
||||||
public class RenderTypeMixin {
|
|
||||||
@Shadow @Final private static ObjectOpenCustomHashSet<RenderType.CompositeRenderType> INSTANCES;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason synchronize, can be accessed by multiple mods during modloading
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
private static RenderType.CompositeRenderType memoize(String name, VertexFormat format, int drawMode, int bufferSize, boolean useDelegate, boolean needsSorting, RenderType.CompositeState renderState) {
|
|
||||||
synchronized (INSTANCES){
|
|
||||||
return INSTANCES.addOrGet(new RenderType.CompositeRenderType(name, format, drawMode, bufferSize, useDelegate, needsSorting, renderState));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.bugfix.packet_leak;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
|
||||||
import org.embeddedt.modernfix.duck.IClientNetHandler;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(ClientPacketListener.class)
|
|
||||||
public class ClientPlayNetHandlerMixin implements IClientNetHandler {
|
|
||||||
private FriendlyByteBuf savedCopy = null;
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason Release the packet buffer at the end. Needed in f
|
|
||||||
*/
|
|
||||||
@Redirect(method = "handleCustomPayload", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundCustomPayloadPacket;getData()Lnet/minecraft/network/FriendlyByteBuf;"))
|
|
||||||
private FriendlyByteBuf saveCopyForRelease(ClientboundCustomPayloadPacket instance) {
|
|
||||||
FriendlyByteBuf copy = instance.getData();
|
|
||||||
savedCopy = copy;
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FriendlyByteBuf getCopiedCustomBuffer() {
|
|
||||||
FriendlyByteBuf copy = savedCopy;
|
|
||||||
savedCopy = null;
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.bugfix.packet_leak;
|
|
||||||
|
|
||||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.embeddedt.modernfix.duck.IClientNetHandler;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(ClientboundCustomPayloadPacket.class)
|
|
||||||
public class SCustomPayloadPlayPacketMixin {
|
|
||||||
@Shadow private FriendlyByteBuf data;
|
|
||||||
|
|
||||||
private boolean needsRelease;
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/network/FriendlyByteBuf;)V", at = @At("RETURN"))
|
|
||||||
private void markNotOwned(ResourceLocation pIdentifier, FriendlyByteBuf pData, CallbackInfo ci) {
|
|
||||||
this.needsRelease = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "read", at = @At("RETURN"))
|
|
||||||
private void markOwned(FriendlyByteBuf p_148837_1_, CallbackInfo ci) {
|
|
||||||
this.needsRelease = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "handle(Lnet/minecraft/network/protocol/game/ClientGamePacketListener;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientGamePacketListener;handleCustomPayload(Lnet/minecraft/network/protocol/game/ClientboundCustomPayloadPacket;)V"))
|
|
||||||
private void handleAndFree(ClientGamePacketListener instance, ClientboundCustomPayloadPacket sCustomPayloadPlayPacket) {
|
|
||||||
try {
|
|
||||||
instance.handleCustomPayload(sCustomPayloadPlayPacket);
|
|
||||||
} finally {
|
|
||||||
FriendlyByteBuf copied = ((IClientNetHandler)instance).getCopiedCustomBuffer();
|
|
||||||
if(copied != null)
|
|
||||||
copied.release();
|
|
||||||
}
|
|
||||||
if(this.needsRelease)
|
|
||||||
this.data.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.bugfix.structure_manager_crash;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Mixin(StructureManager.class)
|
|
||||||
public class StructureManagerMixin {
|
|
||||||
@Shadow @Final private Map<ResourceLocation, StructureTemplate> structureRepository;
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
|
||||||
private void makeStructuresSafe(ResourceManager arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer, CallbackInfo ci) {
|
|
||||||
this.structureRepository = Collections.synchronizedMap(this.structureRepository);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,8 @@ package org.embeddedt.modernfix.mixin.feature.measure_time;
|
||||||
|
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import net.minecraft.server.Bootstrap;
|
import net.minecraft.server.Bootstrap;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.slf4j.Logger;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package org.embeddedt.modernfix.mixin.feature.measure_time;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Function4;
|
import com.mojang.datafixers.util.Function4;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.server.WorldStem;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.world.level.DataPackConfig;
|
import net.minecraft.world.level.DataPackConfig;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
|
@ -21,19 +22,19 @@ import java.util.function.Function;
|
||||||
public class MinecraftMixin {
|
public class MinecraftMixin {
|
||||||
private long datapackReloadStartTime;
|
private long datapackReloadStartTime;
|
||||||
|
|
||||||
@Inject(method = "makeServerStem", at = @At(value = "HEAD"))
|
@Inject(method = "makeWorldStem", at = @At(value = "HEAD"))
|
||||||
private void recordReloadStart(RegistryAccess.RegistryHolder p_238189_1_, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> p_238189_2_, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> p_238189_3_, boolean p_238189_4_, LevelStorageSource.LevelStorageAccess p_238189_5_, CallbackInfoReturnable<Minecraft.ServerStem> cir) {
|
private void recordReloadStart(CallbackInfoReturnable<WorldStem> cir) {
|
||||||
datapackReloadStartTime = System.nanoTime();
|
datapackReloadStartTime = System.nanoTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "makeServerStem", at = @At(value = "RETURN"))
|
@Inject(method = "makeWorldStem", at = @At(value = "RETURN"))
|
||||||
private void recordReloadEnd(RegistryAccess.RegistryHolder p_238189_1_, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> p_238189_2_, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> p_238189_3_, boolean p_238189_4_, LevelStorageSource.LevelStorageAccess p_238189_5_, CallbackInfoReturnable<Minecraft.ServerStem> cir) {
|
private void recordReloadEnd(CallbackInfoReturnable<WorldStem> cir) {
|
||||||
float timeSpentReloading = ((float)(System.nanoTime() - datapackReloadStartTime) / 1000000000f);
|
float timeSpentReloading = ((float)(System.nanoTime() - datapackReloadStartTime) / 1000000000f);
|
||||||
ModernFix.LOGGER.warn("Datapack reload took " + timeSpentReloading + " seconds.");
|
ModernFix.LOGGER.warn("Datapack reload took " + timeSpentReloading + " seconds.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "loadWorld", at = @At("HEAD"), remap = false)
|
@Inject(method = "doLoadLevel", at = @At("HEAD"), remap = false)
|
||||||
private void recordWorldLoadStart(String worldName, RegistryAccess.RegistryHolder dynamicRegistries, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> levelSaveToDatapackFunction, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> quadFunction, boolean vanillaOnly, Minecraft.ExperimentalDialogType selectionType, boolean creating, CallbackInfo ci) {
|
private void recordWorldLoadStart(CallbackInfo ci) {
|
||||||
ModernFixClient.worldLoadStartTime = System.nanoTime();
|
ModernFixClient.worldLoadStartTime = System.nanoTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
|
||||||
|
|
||||||
import mezz.jei.api.IModPlugin;
|
|
||||||
import mezz.jei.api.helpers.IModIdHelper;
|
|
||||||
import mezz.jei.config.*;
|
|
||||||
import mezz.jei.config.sorting.RecipeCategorySortingConfig;
|
|
||||||
import mezz.jei.events.EventBusHelper;
|
|
||||||
import mezz.jei.events.PlayerJoinedWorldEvent;
|
|
||||||
import mezz.jei.gui.textures.Textures;
|
|
||||||
import mezz.jei.ingredients.IIngredientSorter;
|
|
||||||
import mezz.jei.startup.ClientLifecycleHandler;
|
|
||||||
import mezz.jei.startup.JeiStarter;
|
|
||||||
import mezz.jei.startup.NetworkHandler;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.jei.async.JEILoadingInterruptedException;
|
|
||||||
import org.embeddedt.modernfix.jei.async.JEIReloadThread;
|
|
||||||
import org.embeddedt.modernfix.util.JEIUtil;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(ClientLifecycleHandler.class)
|
|
||||||
public class ClientLifecycleHandlerMixin {
|
|
||||||
@Shadow(remap = false) @Final private JeiStarter starter;
|
|
||||||
@Shadow(remap = false) @Final private List<IModPlugin> plugins;
|
|
||||||
@Shadow(remap = false) @Final private Textures textures;
|
|
||||||
@Shadow(remap = false) @Final private IClientConfig clientConfig;
|
|
||||||
@Shadow(remap = false) @Final private IEditModeConfig editModeConfig;
|
|
||||||
@Shadow(remap = false) @Final private IngredientFilterConfig ingredientFilterConfig;
|
|
||||||
@Shadow(remap = false) @Final private WorldConfig worldConfig;
|
|
||||||
@Shadow(remap = false) @Final private BookmarkConfig bookmarkConfig;
|
|
||||||
@Shadow(remap = false) @Final private IModIdHelper modIdHelper;
|
|
||||||
@Shadow(remap = false) @Final private RecipeCategorySortingConfig recipeCategorySortingConfig;
|
|
||||||
@Shadow(remap = false) @Final private IIngredientSorter ingredientSorter;
|
|
||||||
private volatile JEIReloadThread reloadThread = null;
|
|
||||||
@Inject(method = "setupJEI", at = @At(value = "INVOKE", target = "Lmezz/jei/startup/ClientLifecycleHandler;startJEI()V"), cancellable = true, remap = false)
|
|
||||||
private void startAsync(CallbackInfo ci) {
|
|
||||||
ci.cancel();
|
|
||||||
startJEIAsync(() -> Minecraft.getInstance().execute(() -> EventBusHelper.post(new PlayerJoinedWorldEvent())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason force JEI starts to be asynchronous
|
|
||||||
*/
|
|
||||||
@Overwrite(remap = false)
|
|
||||||
public void startJEI() {
|
|
||||||
startJEIAsync(() -> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
|
||||||
private void setupCancellationHandler(NetworkHandler networkHandler, Textures textures, CallbackInfo ci) {
|
|
||||||
EventBusHelper.addListener(this, ClientPlayerNetworkEvent.LoggedOutEvent.class, event -> cancelPreviousStart());
|
|
||||||
JEIUtil.registerLoadingRenderer(() -> reloadThread != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelPreviousStart() {
|
|
||||||
JEIReloadThread currentReloadThread = reloadThread;
|
|
||||||
if(currentReloadThread != null) {
|
|
||||||
currentReloadThread.requestStop();
|
|
||||||
Minecraft.getInstance().managedBlock(currentReloadThread::isAlive);
|
|
||||||
reloadThread = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int numReloads = 1;
|
|
||||||
|
|
||||||
private void startJEIAsync(Runnable whenFinishedCb) {
|
|
||||||
ModernFix.LOGGER.info("JEI restart triggered. Waiting for previous thread to die.");
|
|
||||||
cancelPreviousStart();
|
|
||||||
ModernFix.LOGGER.info("Starting new JEI thread.");
|
|
||||||
JEIReloadThread newThread = new JEIReloadThread(() -> {
|
|
||||||
try {
|
|
||||||
starter.start(
|
|
||||||
plugins,
|
|
||||||
textures,
|
|
||||||
clientConfig,
|
|
||||||
editModeConfig,
|
|
||||||
ingredientFilterConfig,
|
|
||||||
worldConfig,
|
|
||||||
bookmarkConfig,
|
|
||||||
modIdHelper,
|
|
||||||
recipeCategorySortingConfig,
|
|
||||||
ingredientSorter);
|
|
||||||
} catch(JEILoadingInterruptedException e) {
|
|
||||||
ModernFix.LOGGER.warn("JEI loading interrupted prematurely (this is normal)");
|
|
||||||
}
|
|
||||||
whenFinishedCb.run();
|
|
||||||
reloadThread = null;
|
|
||||||
}, "ModernFix JEI Reload Thread " + numReloads++);
|
|
||||||
newThread.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
reloadThread = newThread;
|
|
||||||
newThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
|
||||||
|
|
||||||
import mezz.jei.api.ingredients.IIngredientType;
|
|
||||||
import mezz.jei.api.runtime.IIngredientManager;
|
|
||||||
import mezz.jei.gui.ingredients.IIngredientListElement;
|
|
||||||
import mezz.jei.ingredients.IngredientListElementFactory;
|
|
||||||
import net.minecraft.core.NonNullList;
|
|
||||||
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(IngredientListElementFactory.class)
|
|
||||||
public class IngredientListElementFactoryMixin {
|
|
||||||
private static int ingredientNum = 0;
|
|
||||||
@Inject(method = "addToBaseList", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/NonNullList;add(Ljava/lang/Object;)Z"))
|
|
||||||
private static void checkForInterrupt(NonNullList<IIngredientListElement<?>> baseList, IIngredientManager ingredientManager, IIngredientType ingredientType, CallbackInfo ci) {
|
|
||||||
if((ingredientNum++ % 100) == 0)
|
|
||||||
IAsyncJeiStarter.checkForLoadInterruption();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
|
||||||
|
|
||||||
import mezz.jei.api.IModPlugin;
|
|
||||||
import mezz.jei.api.helpers.IModIdHelper;
|
|
||||||
import mezz.jei.config.*;
|
|
||||||
import mezz.jei.config.sorting.RecipeCategorySortingConfig;
|
|
||||||
import mezz.jei.gui.textures.Textures;
|
|
||||||
import mezz.jei.ingredients.IIngredientSorter;
|
|
||||||
import mezz.jei.load.PluginCaller;
|
|
||||||
import mezz.jei.startup.JeiStarter;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
|
|
||||||
import org.embeddedt.modernfix.jei.async.JEILoadingInterruptedException;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CancellationException;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Mixin(JeiStarter.class)
|
|
||||||
public class JeiStarterMixin {
|
|
||||||
@Shadow(remap = false) private boolean started;
|
|
||||||
|
|
||||||
@Inject(method = "start", at = @At(value = "INVOKE", target = "Lmezz/jei/util/ErrorUtil;checkNotEmpty(Ljava/util/Collection;Ljava/lang/String;)V", ordinal = 0, shift = At.Shift.AFTER), remap = false)
|
|
||||||
private void setStartedFlag(List<IModPlugin> plugins, Textures textures, IClientConfig clientConfig, IEditModeConfig editModeConfig, IIngredientFilterConfig ingredientFilterConfig, IWorldConfig worldConfig, BookmarkConfig bookmarkConfig, IModIdHelper modIdHelper, RecipeCategorySortingConfig recipeCategorySortingConfig, IIngredientSorter ingredientSorter, CallbackInfo ci) {
|
|
||||||
/* We need to set this ASAP so the reload system will restart the async load if needed */
|
|
||||||
started = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "start", at = @At(value = "INVOKE", target = "Lmezz/jei/load/PluginCaller;callOnPlugins(Ljava/lang/String;Ljava/util/List;Ljava/util/function/Consumer;)V"), remap = false)
|
|
||||||
private void callOnPluginsViaMainThread(String title, List<IModPlugin> plugins, Consumer<IModPlugin> func) {
|
|
||||||
PluginCaller.callOnPlugins(title, plugins, plugin -> {
|
|
||||||
try {
|
|
||||||
Minecraft.getInstance().executeBlocking(() -> func.accept(plugin));
|
|
||||||
} catch(CancellationException | CompletionException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
|
||||||
|
|
||||||
import mezz.jei.api.IModPlugin;
|
|
||||||
import mezz.jei.load.PluginCaller;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
|
||||||
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CancellationException;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Mixin(PluginCaller.class)
|
|
||||||
public class PluginCallerMixin {
|
|
||||||
@Inject(method = "callOnPlugins", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;hasNext()Z"), remap = false)
|
|
||||||
private static void checkForInterrupt(String title, List<IModPlugin> plugins, Consumer<IModPlugin> func, CallbackInfo ci) {
|
|
||||||
IAsyncJeiStarter.checkForLoadInterruption();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked","rawtypes"})
|
|
||||||
@Redirect(method = "callOnPlugins", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), remap = false)
|
|
||||||
private static void runOnMainThreadIfNeeded(Consumer instance, Object pluginObj) {
|
|
||||||
IModPlugin plugin = (IModPlugin)pluginObj;
|
|
||||||
if(ModernFixConfig.jeiPluginBlacklist.contains(plugin.getPluginUid())) {
|
|
||||||
ModernFix.LOGGER.warn("Going to main thread for " + plugin.getPluginUid());
|
|
||||||
Minecraft.getInstance().executeBlocking(() -> instance.accept(plugin));
|
|
||||||
} else {
|
|
||||||
instance.accept(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
|
||||||
import mezz.jei.recipes.RecipeManagerInternal;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.embeddedt.modernfix.jei.async.IAsyncJeiStarter;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(RecipeManagerInternal.class)
|
|
||||||
public class RecipeManagerInternalMixin {
|
|
||||||
@Inject(method = "addRecipes", at = @At(value = "INVOKE", target = "Lmezz/jei/recipes/RecipeManagerInternal;addRecipeTyped(Ljava/lang/Object;Lnet/minecraft/resources/ResourceLocation;)V"))
|
|
||||||
private void checkForInterrupt(ImmutableListMultimap<ResourceLocation, Object> recipes, CallbackInfo ci) {
|
|
||||||
IAsyncJeiStarter.checkForLoadInterruption();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.commands.CommandSource;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(CommandSourceStack.class)
|
|
||||||
public interface CommandSourceStackAccess {
|
|
||||||
@Accessor
|
|
||||||
CommandSource getSource();
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.animal.Dolphin;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
@Mixin(targets = "net.minecraft.world.entity.animal.Dolphin$DolphinSwimToTreasureGoal")
|
|
||||||
public class DolphinSwimToTreasureGoalMixin {
|
|
||||||
@Final
|
|
||||||
@Shadow
|
|
||||||
private Dolphin dolphin;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private boolean stuck;
|
|
||||||
|
|
||||||
private AsyncLocator.LocateTask<BlockPos> locateTask = null;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Intercept DolphinSwimToTreasureGoal#start call right before it calls ServerLevel#findNearestMapFeature to pass
|
|
||||||
the logic over to an async task.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "start",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;findNearestMapFeature(Lnet/minecraft/world/level/levelgen/feature/StructureFeature;Lnet/minecraft/core/BlockPos;IZ)Lnet/minecraft/core/BlockPos;"
|
|
||||||
),
|
|
||||||
cancellable = true,
|
|
||||||
locals = LocalCapture.CAPTURE_FAILSOFT
|
|
||||||
)
|
|
||||||
public void findTreasureAsync(CallbackInfo ci, ServerLevel level, BlockPos blockpos) {
|
|
||||||
ModernFix.LOGGER.debug("Intercepted DolphinSwimToTreasureGoal#start call");
|
|
||||||
handleFindTreasureAsync(level, blockpos);
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Intercept DolphinSwimToTreasureGoal#canContinueToUse to return true if an async locating task is ongoing so that
|
|
||||||
the goal isn't ended early due to no treasure pos being set yet.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "canContinueToUse",
|
|
||||||
at = @At(value = "HEAD"),
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
public void continueToUseIfLocatingTreasure(CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
if (locateTask != null) {
|
|
||||||
ModernFix.LOGGER.debug("Locating task ongoing - returning true for continueToUse()");
|
|
||||||
cir.setReturnValue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(
|
|
||||||
method = "stop",
|
|
||||||
at = @At(value = "HEAD")
|
|
||||||
)
|
|
||||||
public void stopLocatingTreasure(CallbackInfo ci) {
|
|
||||||
if (locateTask != null) {
|
|
||||||
ModernFix.LOGGER.debug("Locating task ongoing - cancelling during stop()");
|
|
||||||
locateTask.cancel();
|
|
||||||
locateTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Intercept DolphinSwimToTreasureGoal#tick to return early if an async locating task is ongoing so that the
|
|
||||||
dolphin doesn't try to go towards an old treasure position.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "tick",
|
|
||||||
at = @At(value = "HEAD"),
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
public void skipTickingIfLocatingTreasure(CallbackInfo ci) {
|
|
||||||
if (locateTask != null) {
|
|
||||||
ModernFix.LOGGER.debug("Locating task ongoing - skipping tick()");
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleFindTreasureAsync(ServerLevel level, BlockPos blockPos) {
|
|
||||||
locateTask = AsyncLocator.locateLevel(level, ImmutableSet.of(StructureFeature.OCEAN_RUIN, StructureFeature.SHIPWRECK), blockPos, 50, false)
|
|
||||||
.thenOnServerThread(pos -> handleLocationFound(level, pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLocationFound(ServerLevel level, BlockPos pos) {
|
|
||||||
locateTask = null;
|
|
||||||
if (pos != null) {
|
|
||||||
ModernFix.LOGGER.debug("Location found - updating dolphin treasure pos");
|
|
||||||
dolphin.setTreasurePos(pos);
|
|
||||||
level.broadcastEntityEvent(dolphin, (byte) 38);
|
|
||||||
} else {
|
|
||||||
ModernFix.LOGGER.debug("No location found - marking dolphin as stuck");
|
|
||||||
stuck = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.advancements.critereon.UsedEnderEyeTrigger;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stat;
|
|
||||||
import net.minecraft.world.InteractionHand;
|
|
||||||
import net.minecraft.world.InteractionResultHolder;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
|
||||||
import net.minecraft.world.item.EnderEyeItem;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.phys.HitResult;
|
|
||||||
import org.embeddedt.modernfix.structure.logic.EnderEyeItemLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
@Mixin(EnderEyeItem.class)
|
|
||||||
public class EnderEyeItemMixin {
|
|
||||||
/*
|
|
||||||
Intercept EnderEyeItem#use call and return BlockPos.ZERO instead. It won't be used in the EyeOfEnder entity
|
|
||||||
created later either, as we need to set the actual location ourselves.
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "use",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/level/chunk/ChunkGenerator;findNearestMapFeature(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/levelgen/feature/StructureFeature;Lnet/minecraft/core/BlockPos;IZ)Lnet/minecraft/core/BlockPos;"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
public BlockPos levelFindNearestMapFeature(
|
|
||||||
ChunkGenerator generator,
|
|
||||||
ServerLevel level,
|
|
||||||
StructureFeature<?> structureFeature,
|
|
||||||
BlockPos pPos,
|
|
||||||
int pRadius,
|
|
||||||
boolean pSkipExistingChunks
|
|
||||||
) {
|
|
||||||
return BlockPos.ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the async locate task here so we have the eye of ender entity for context
|
|
||||||
@Inject(
|
|
||||||
method = "use",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/entity/projectile/EyeOfEnder;setItem(Lnet/minecraft/world/item/ItemStack;)V"
|
|
||||||
),
|
|
||||||
locals = LocalCapture.CAPTURE_FAILEXCEPTION
|
|
||||||
)
|
|
||||||
public void startAsyncLocateTask(
|
|
||||||
Level pLevel,
|
|
||||||
Player pPlayer,
|
|
||||||
InteractionHand pHand,
|
|
||||||
CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir,
|
|
||||||
ItemStack itemstack,
|
|
||||||
HitResult hitresult,
|
|
||||||
BlockPos blockpos,
|
|
||||||
EyeOfEnder eyeofender
|
|
||||||
) {
|
|
||||||
EnderEyeItemLogic.locateAsync((ServerLevel)pLevel, pPlayer, eyeofender, (EnderEyeItem) (Object) this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(
|
|
||||||
method = "use",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/entity/projectile/EyeOfEnder;signalTo(Lnet/minecraft/core/BlockPos;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
public void eyeOfEnderSignalTo(EyeOfEnder eyeOfEnder, BlockPos blockpos) {
|
|
||||||
// Do nothing - we'll do this later if a location is found
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(
|
|
||||||
method = "use",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/advancements/critereon/UsedEnderEyeTrigger;trigger(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/core/BlockPos;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
public void triggerUsedEnderEyeCriteria(UsedEnderEyeTrigger trigger, ServerPlayer player, BlockPos pos) {
|
|
||||||
// Do nothing - we'll do this later if a location is found
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(
|
|
||||||
method = "use",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/entity/player/Player;awardStat(Lnet/minecraft/stats/Stat;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
public void playerAwardStat(Player instance, Stat<?> pStat) {
|
|
||||||
// Do nothing - we'll do this later if a location is found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapDecoration;
|
|
||||||
import net.minecraft.world.level.storage.loot.LootContext;
|
|
||||||
import net.minecraft.world.level.storage.loot.functions.ExplorationMapFunction;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import org.embeddedt.modernfix.structure.logic.ExplorationMapFunctionLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
@Mixin(ExplorationMapFunction.class)
|
|
||||||
public class ExplorationMapFunctionMixin {
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
StructureFeature<?> destination;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
MapDecoration.Type mapDecoration;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
byte zoom;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
int searchRadius;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
boolean skipKnownStructures;
|
|
||||||
|
|
||||||
@Inject(
|
|
||||||
method = "run",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;findNearestMapFeature(Lnet/minecraft/world/level/levelgen/feature/StructureFeature;Lnet/minecraft/core/BlockPos;IZ)Lnet/minecraft/core/BlockPos;"
|
|
||||||
),
|
|
||||||
locals = LocalCapture.CAPTURE_FAILSOFT,
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
public void updateMapAsync(
|
|
||||||
ItemStack pStack,
|
|
||||||
LootContext pContext,
|
|
||||||
CallbackInfoReturnable<ItemStack> cir,
|
|
||||||
Vec3 vec3,
|
|
||||||
ServerLevel serverlevel
|
|
||||||
) {
|
|
||||||
ItemStack mapStack = ExplorationMapFunctionLogic.updateMapAsync(
|
|
||||||
serverlevel, new BlockPos(vec3), zoom, searchRadius, skipKnownStructures, mapDecoration, destination
|
|
||||||
);
|
|
||||||
cir.setReturnValue(mapStack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(EyeOfEnder.class)
|
|
||||||
public interface EyeOfEnderAccess {
|
|
||||||
@Accessor
|
|
||||||
void setLife(int life);
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
|
||||||
import org.embeddedt.modernfix.structure.logic.EyeOfEnderData;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(EyeOfEnder.class)
|
|
||||||
public class EyeOfEnderMixin implements EyeOfEnderData {
|
|
||||||
private boolean locateTaskOngoing = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLocateTaskOngoing(boolean locateTaskOngoing) {
|
|
||||||
this.locateTaskOngoing = locateTaskOngoing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Intercept EyeOfEnder#tick call and return after the super call if there's an ongoing locate task. This is to
|
|
||||||
prevent the entity from moving or dying until we have a location result.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "tick",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/entity/Entity;tick()V",
|
|
||||||
shift = At.Shift.AFTER
|
|
||||||
),
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
public void skipTick(CallbackInfo ci) {
|
|
||||||
if (locateTaskOngoing) {
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
|
||||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
|
||||||
import net.minecraft.server.commands.LocateCommand;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(LocateCommand.class)
|
|
||||||
public interface LocateCommandAccess {
|
|
||||||
@Accessor("ERROR_FAILED")
|
|
||||||
static SimpleCommandExceptionType getErrorFailed() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.commands.CommandSource;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.commands.LocateCommand;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import org.embeddedt.modernfix.structure.logic.LocateCommandLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
@Mixin(LocateCommand.class)
|
|
||||||
public class LocateCommandMixin {
|
|
||||||
@Inject(
|
|
||||||
method = "locate",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;findNearestMapFeature(Lnet/minecraft/world/level/levelgen/feature/StructureFeature;Lnet/minecraft/core/BlockPos;IZ)Lnet/minecraft/core/BlockPos;"
|
|
||||||
),
|
|
||||||
cancellable = true,
|
|
||||||
locals = LocalCapture.CAPTURE_FAILSOFT
|
|
||||||
)
|
|
||||||
private static void findLocationAsync(CommandSourceStack sourceStack, StructureFeature<?> feature, CallbackInfoReturnable<Integer> cir) {
|
|
||||||
CommandSource source = ((CommandSourceStackAccess) sourceStack).getSource();
|
|
||||||
if (source instanceof ServerPlayer || source instanceof MinecraftServer) {
|
|
||||||
LocateCommandLogic.locateAsync(sourceStack, feature);
|
|
||||||
cir.setReturnValue(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.MapItem;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
|
||||||
|
|
||||||
@Mixin(MapItem.class)
|
|
||||||
public interface MapItemAccess {
|
|
||||||
@Invoker
|
|
||||||
static MapItemSavedData callCreateAndStoreSavedData(ItemStack pStack, Level pLevel, int pX, int pZ, int pScale, boolean pTrackingPosition, boolean pUnlimitedTracking, ResourceKey<Level> pDimension) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.world.item.trading.MerchantOffer;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Mutable;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(MerchantOffer.class)
|
|
||||||
public interface MerchantOfferAccess {
|
|
||||||
@Mutable
|
|
||||||
@Accessor
|
|
||||||
void setMaxUses(int maxUses);
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.async_locator;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.trading.MerchantOffer;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapDecoration;
|
|
||||||
import org.embeddedt.modernfix.structure.logic.MerchantLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
@Mixin(targets = "net.minecraft.world.entity.npc.VillagerTrades$TreasureMapForEmeralds")
|
|
||||||
public class TreasureMapForEmeraldsMixin {
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private int emeraldCost;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private MapDecoration.Type destinationType;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private int maxUses;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private int villagerXp;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private StructureFeature<?> destination;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Intercept TreasureMapForEmeralds#getOffer call right before it calls ServerLevel#findNearestMapFeature to pass
|
|
||||||
the logic over to an async task. Instead of returning the complete map or null, we'll have to always return an
|
|
||||||
incomplete filled map and later update it with the details when we have them.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "getOffer",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;findNearestMapFeature(Lnet/minecraft/world/level/levelgen/feature/StructureFeature;Lnet/minecraft/core/BlockPos;IZ)Lnet/minecraft/core/BlockPos;"
|
|
||||||
),
|
|
||||||
cancellable = true
|
|
||||||
)
|
|
||||||
public void updateMapAsync(Entity pTrader, Random pRand, CallbackInfoReturnable<MerchantOffer> callbackInfo) {
|
|
||||||
String displayName = "filled_map." + this.destination.getFeatureName().toLowerCase(Locale.ROOT);
|
|
||||||
MerchantOffer offer = MerchantLogic.updateMapAsync(
|
|
||||||
pTrader, emeraldCost, displayName, destinationType, maxUses, villagerXp, destination
|
|
||||||
);
|
|
||||||
if (offer != null) {
|
|
||||||
callbackInfo.setReturnValue(offer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.blast_search_trees;
|
package org.embeddedt.modernfix.mixin.perf.blast_search_trees;
|
||||||
|
|
||||||
import mezz.jei.ingredients.IIngredientListElementInfo;
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
import mezz.jei.ingredients.IngredientFilter;
|
import mezz.jei.common.ingredients.IngredientFilter;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
|
|
@ -10,5 +10,5 @@ import java.util.List;
|
||||||
@Mixin(IngredientFilter.class)
|
@Mixin(IngredientFilter.class)
|
||||||
public interface IngredientFilterInvoker {
|
public interface IngredientFilterInvoker {
|
||||||
@Invoker(remap = false)
|
@Invoker(remap = false)
|
||||||
List<IIngredientListElementInfo<?>> invokeGetIngredientListUncached(String filterText);
|
List<ITypedIngredient<?>> invokeGetIngredientListUncached(String filterText);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.boost_worker_count;
|
|
||||||
|
|
||||||
import net.minecraft.Util;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Constant;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
|
||||||
|
|
||||||
@Mixin(Util.class)
|
|
||||||
public class UtilMixin {
|
|
||||||
@ModifyConstant(method = "makeExecutor", constant = @Constant(intValue = 7))
|
|
||||||
private static int useHigherThreadCount(int old) {
|
|
||||||
String requestedMax = System.getProperty("max.bg.threads");
|
|
||||||
if(requestedMax != null) {
|
|
||||||
try {
|
|
||||||
int newMax = Integer.parseInt(requestedMax);
|
|
||||||
if(newMax >= 1 && newMax <= 255)
|
|
||||||
return newMax;
|
|
||||||
} catch(NumberFormatException e) {
|
|
||||||
ModernFix.LOGGER.error("max.bg.threads is not a number");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.compress_biome_container;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ShortMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ShortOpenHashMap;
|
|
||||||
import net.minecraft.util.BitStorage;
|
|
||||||
import net.minecraft.core.IdMap;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import org.spongepowered.asm.mixin.*;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(ChunkBiomeContainer.class)
|
|
||||||
public class MixinBiomeContainer {
|
|
||||||
@Mutable
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private Biome[] biomes;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private IdMap<Biome> biomeRegistry;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private static int WIDTH_BITS;
|
|
||||||
|
|
||||||
private Biome[] palette;
|
|
||||||
private BitStorage intArray;
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;[I)V", at = @At("RETURN"))
|
|
||||||
private void reinit1(IdMap p_i241970_1_, int[] p_i241970_2_, CallbackInfo ci) {
|
|
||||||
this.createCompact();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;[Lnet/minecraft/world/level/biome/Biome;)V", at = @At("RETURN"))
|
|
||||||
private void reinit2(IdMap p_i241971_1_, Biome[] p_i241971_2_, CallbackInfo ci) {
|
|
||||||
this.createCompact();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/biome/BiomeSource;)V", at = @At("RETURN"))
|
|
||||||
private void reinit3(IdMap p_i241968_1_, ChunkPos p_i241968_2_, BiomeSource p_i241968_3_, CallbackInfo ci) {
|
|
||||||
this.createCompact();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/biome/BiomeSource;[I)V", at = @At("RETURN"))
|
|
||||||
private void reinit4(IdMap p_i241969_1_, ChunkPos p_i241969_2_, BiomeSource p_i241969_3_, int[] p_i241969_4_, CallbackInfo ci) {
|
|
||||||
this.createCompact();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createCompact() {
|
|
||||||
if (this.intArray != null || this.biomes[0] == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference2ShortOpenHashMap<Biome> paletteTable = this.createPalette();
|
|
||||||
Biome[] paletteIndexed = new Biome[paletteTable.size()];
|
|
||||||
|
|
||||||
for (Reference2ShortMap.Entry<Biome> entry : paletteTable.reference2ShortEntrySet()) {
|
|
||||||
paletteIndexed[entry.getShortValue()] = entry.getKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
int packedIntSize = Math.max(2, Mth.ceillog2(paletteTable.size()));
|
|
||||||
BitStorage integerArray = new BitStorage(packedIntSize, ChunkBiomeContainer.BIOMES_SIZE);
|
|
||||||
|
|
||||||
Biome prevBiome = null;
|
|
||||||
short prevId = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < this.biomes.length; i++) {
|
|
||||||
Biome biome = this.biomes[i];
|
|
||||||
short id;
|
|
||||||
|
|
||||||
if (prevBiome == biome) {
|
|
||||||
id = prevId;
|
|
||||||
} else {
|
|
||||||
id = paletteTable.getShort(biome);
|
|
||||||
|
|
||||||
if (id < 0) {
|
|
||||||
throw new IllegalStateException("Palette is missing entry: " + biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
prevId = id;
|
|
||||||
prevBiome = biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
integerArray.set(i, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.palette = paletteIndexed;
|
|
||||||
this.intArray = integerArray;
|
|
||||||
this.biomes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Reference2ShortOpenHashMap<Biome> createPalette() {
|
|
||||||
Reference2ShortOpenHashMap<Biome> map = new Reference2ShortOpenHashMap<>();
|
|
||||||
map.defaultReturnValue(Short.MIN_VALUE);
|
|
||||||
|
|
||||||
Biome prevObj = null;
|
|
||||||
short id = 0;
|
|
||||||
|
|
||||||
for (Biome obj : this.biomes) {
|
|
||||||
if (obj == prevObj) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.getShort(obj) < 0) {
|
|
||||||
map.put(obj, id++);
|
|
||||||
}
|
|
||||||
|
|
||||||
prevObj = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author JellySquid
|
|
||||||
* @reason Use paletted lookup
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public int[] writeBiomes() {
|
|
||||||
int size = this.intArray.getSize();
|
|
||||||
int[] array = new int[size];
|
|
||||||
|
|
||||||
for(int i = 0; i < size; ++i) {
|
|
||||||
array[i] = this.biomeRegistry.getId(this.palette[this.intArray.get(i)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author JellySquid
|
|
||||||
* @reason Use paletted lookup
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
|
||||||
int x = biomeX & ChunkBiomeContainer.HORIZONTAL_MASK;
|
|
||||||
int y = Mth.clamp(biomeY, 0, ChunkBiomeContainer.VERTICAL_MASK);
|
|
||||||
int z = biomeZ & ChunkBiomeContainer.HORIZONTAL_MASK;
|
|
||||||
|
|
||||||
return this.palette[this.intArray.get(y << WIDTH_BITS + WIDTH_BITS | z << WIDTH_BITS | x)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.datapack_reload_exceptions;
|
package org.embeddedt.modernfix.mixin.perf.datapack_reload_exceptions;
|
||||||
|
|
||||||
import net.minecraft.world.level.storage.loot.LootTables;
|
import net.minecraft.world.level.storage.loot.LootTables;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(LootTables.class)
|
@Mixin(LootTables.class)
|
||||||
public class LootTableManagerMixin {
|
public class LootTableManagerMixin {
|
||||||
@Redirect(method = "*(Lnet/minecraft/resources/IResourceManager;Lcom/google/common/collect/ImmutableMap$Builder;Lnet/minecraft/util/ResourceLocation;Lcom/google/gson/JsonElement;)V",
|
@Redirect(method = "*(Lnet/minecraft/server/packs/resources/ResourceManager;Lcom/google/common/collect/ImmutableMap$Builder;Lnet/minecraft/resources/ResourceLocation;Lcom/google/gson/JsonElement;)V",
|
||||||
at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
|
at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
|
||||||
private void logWithoutStacktrace(Logger instance, String s, Object location, Object exc) {
|
private void logWithoutStacktrace(Logger instance, String s, Object location, Object exc) {
|
||||||
instance.error(s + ": {}", location, exc.toString());
|
instance.error(s + ": {}", location, exc.toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.datapack_reload_exceptions;
|
package org.embeddedt.modernfix.mixin.perf.datapack_reload_exceptions;
|
||||||
|
|
||||||
import net.minecraft.world.item.crafting.RecipeManager;
|
import net.minecraft.world.item.crafting.RecipeManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(RecipeManager.class)
|
@Mixin(RecipeManager.class)
|
||||||
public class RecipeManagerMixin {
|
public class RecipeManagerMixin {
|
||||||
@Redirect(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
|
@Redirect(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
|
||||||
private void silenceException(Logger instance, String s, Object location, Object exc) {
|
private void silenceException(Logger instance, String s, Object location, Object exc) {
|
||||||
instance.error(s + ": {}", location, exc.toString());
|
instance.error(s + ": {}", location, exc.toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,6 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
|
|
||||||
@Shadow @Nullable private AtlasSet atlasSet;
|
@Shadow @Nullable private AtlasSet atlasSet;
|
||||||
|
|
||||||
@Shadow @Nullable public abstract BakedModel getBakedModel(ResourceLocation pLocation, ModelState pTransform, Function<Material, TextureAtlasSprite> textureGetter);
|
|
||||||
|
|
||||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
||||||
|
|
||||||
@Shadow @Final private Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
@Shadow @Final private Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
||||||
|
|
@ -136,7 +134,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
TextureAtlas atlastexture = pair.getFirst();
|
TextureAtlas atlastexture = pair.getFirst();
|
||||||
TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond();
|
TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond();
|
||||||
pResourceManager.register(atlastexture.location(), atlastexture);
|
pResourceManager.register(atlastexture.location(), atlastexture);
|
||||||
pResourceManager.bind(atlastexture.location());
|
pResourceManager.bindForSetup(atlastexture.location());
|
||||||
atlastexture.updateFilter(atlastexture$sheetdata);
|
atlastexture.updateFilter(atlastexture$sheetdata);
|
||||||
}
|
}
|
||||||
pProfiler.pop();
|
pProfiler.pop();
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ import net.minecraft.client.renderer.texture.TextureManager;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.client.model.ModelLoader;
|
import net.minecraftforge.client.model.ForgeModelBakery;
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||||
import org.embeddedt.modernfix.models.LazyBakedModel;
|
import org.embeddedt.modernfix.models.LazyBakedModel;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -22,7 +21,6 @@ import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||||
|
|
||||||
|
|
@ -44,7 +42,7 @@ public class ModelManagerMixin {
|
||||||
@Shadow @Final private BlockModelShaper blockModelShaper;
|
@Shadow @Final private BlockModelShaper blockModelShaper;
|
||||||
|
|
||||||
@Inject(method = "prepare(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)Lnet/minecraft/client/resources/model/ModelBakery;", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;endTick()V"), locals = LocalCapture.CAPTURE_FAILHARD)
|
@Inject(method = "prepare(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)Lnet/minecraft/client/resources/model/ModelBakery;", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;endTick()V"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||||
private void fireModelBakeEvent(ResourceManager pResourceManager, ProfilerFiller pProfiler, CallbackInfoReturnable<ModelBakery> cir, ModelLoader pObject) {
|
private void fireModelBakeEvent(ResourceManager pResourceManager, ProfilerFiller pProfiler, CallbackInfoReturnable<ModelBakery> cir, ForgeModelBakery pObject) {
|
||||||
pProfiler.push("modelevent");
|
pProfiler.push("modelevent");
|
||||||
if (this.atlases != null) {
|
if (this.atlases != null) {
|
||||||
Minecraft.getInstance().executeBlocking(() -> {
|
Minecraft.getInstance().executeBlocking(() -> {
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.nuke_empty_chunk_sections;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.UpgradeData;
|
|
||||||
import net.minecraft.world.level.TickList;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Mixin(LevelChunk.class)
|
|
||||||
public class MixinChunk {
|
|
||||||
@Shadow @Final private LevelChunkSection[] sections;
|
|
||||||
|
|
||||||
@Inject(method = "<init>(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/chunk/ChunkBiomeContainer;Lnet/minecraft/world/level/chunk/UpgradeData;Lnet/minecraft/world/level/TickList;Lnet/minecraft/world/level/TickList;J[Lnet/minecraft/world/level/chunk/LevelChunkSection;Ljava/util/function/Consumer;)V",
|
|
||||||
at = @At("RETURN"))
|
|
||||||
private void reinit(Level world, ChunkPos pos, ChunkBiomeContainer container, UpgradeData data,
|
|
||||||
TickList list1, TickList list2, long inhabited,
|
|
||||||
LevelChunkSection[] oldSections, Consumer consumer, CallbackInfo ci) {
|
|
||||||
/* taken from Hydrogen */
|
|
||||||
for(int i = 0; i < this.sections.length; i++) {
|
|
||||||
if(LevelChunkSection.isEmpty(this.sections[i])) {
|
|
||||||
this.sections[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,7 +12,7 @@ public class BooleanPropertyMixin {
|
||||||
* There is no point comparing the immutable sets in any two instances of this class, as they will always be
|
* There is no point comparing the immutable sets in any two instances of this class, as they will always be
|
||||||
* the same.
|
* the same.
|
||||||
*/
|
*/
|
||||||
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z", remap = false))
|
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z", remap = false), remap = false)
|
||||||
private boolean skipEqualityCheck(ImmutableSet instance, Object object) {
|
private boolean skipEqualityCheck(ImmutableSet instance, Object object) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class PropertyMixin {
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason compare hashcodes if generated, use reference equality for speed
|
* @reason compare hashcodes if generated, use reference equality for speed
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite(remap = false)
|
||||||
public boolean equals(Object p_equals_1_) {
|
public boolean equals(Object p_equals_1_) {
|
||||||
if (this == p_equals_1_) {
|
if (this == p_equals_1_) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ public class TransformationMatrixMixin {
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason use cached hashcode if exists
|
* @reason use cached hashcode if exists
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite(remap = false)
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash;
|
int hash;
|
||||||
if(cachedHashCode != null) {
|
if(cachedHashCode != null) {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.preload_block_classes;
|
|
||||||
|
|
||||||
import net.minecraftforge.fml.ModLoadingStage;
|
|
||||||
import net.minecraftforge.registries.GameData;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.embeddedt.modernfix.util.BlockClassPreloader;
|
|
||||||
|
|
||||||
@Mixin(GameData.class)
|
|
||||||
public class GameDataMixin {
|
|
||||||
@Inject(method = "generateRegistryEvents", at = @At("RETURN"), remap = false)
|
|
||||||
private static void preloadBlockClasses(CallbackInfoReturnable<Stream<ModLoadingStage.EventGenerator<?>>> cir) {
|
|
||||||
BlockClassPreloader.preloadClasses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.resourcepacks;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import net.minecraft.server.packs.PackType;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
|
||||||
import net.minecraftforge.fml.packs.ModFileResourcePack;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.FileVisitOption;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Mixin(ModFileResourcePack.class)
|
|
||||||
public abstract class ModFileResourcePackMixin {
|
|
||||||
@Shadow public abstract Set<String> getNamespaces(PackType type);
|
|
||||||
|
|
||||||
@Shadow(remap = false) @Final private ModFile modFile;
|
|
||||||
private EnumMap<PackType, Set<String>> namespacesByType;
|
|
||||||
private EnumMap<PackType, HashMap<String, List<Path>>> rootListingByNamespaceAndType;
|
|
||||||
private Set<String> containedPaths;
|
|
||||||
private boolean useNamespaceCaches;
|
|
||||||
private FileSystem resourcePackFS;
|
|
||||||
private static Joiner slashJoiner = Joiner.on('/');
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
|
||||||
private void cacheResources(ModFile modFile, CallbackInfo ci) {
|
|
||||||
this.resourcePackFS = modFile.getLocator().findPath(modFile, "").getFileSystem();
|
|
||||||
this.useNamespaceCaches = false;
|
|
||||||
this.namespacesByType = new EnumMap<>(PackType.class);
|
|
||||||
for(PackType type : PackType.values()) {
|
|
||||||
this.namespacesByType.put(type, this.getNamespaces(type));
|
|
||||||
}
|
|
||||||
this.useNamespaceCaches = true;
|
|
||||||
this.rootListingByNamespaceAndType = new EnumMap<>(PackType.class);
|
|
||||||
this.containedPaths = new HashSet<>();
|
|
||||||
for(PackType type : PackType.values()) {
|
|
||||||
Set<String> namespaces = this.namespacesByType.get(type);
|
|
||||||
HashMap<String, List<Path>> rootListingForNamespaces = new HashMap<>();
|
|
||||||
for(String namespace : namespaces) {
|
|
||||||
try {
|
|
||||||
Path root = modFile.getLocator().findPath(modFile, type.getDirectory(), namespace).toAbsolutePath();
|
|
||||||
try (Stream<Path> stream = Files.walk(root)) {
|
|
||||||
ArrayList<Path> rootListingPaths = new ArrayList<>();
|
|
||||||
stream
|
|
||||||
.map(path -> root.relativize(path.toAbsolutePath()))
|
|
||||||
.filter(this::isValidCachedResourcePath)
|
|
||||||
.forEach(path -> {
|
|
||||||
if(!path.toString().endsWith(".mcmeta"))
|
|
||||||
rootListingPaths.add(path);
|
|
||||||
String mergedPath = slashJoiner.join(type.getDirectory(), namespace, path);
|
|
||||||
this.containedPaths.add(mergedPath);
|
|
||||||
});
|
|
||||||
rootListingPaths.trimToSize();
|
|
||||||
rootListingForNamespaces.put(namespace, rootListingPaths);
|
|
||||||
}
|
|
||||||
} catch(IOException e) {
|
|
||||||
rootListingForNamespaces.put(namespace, Collections.emptyList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.rootListingByNamespaceAndType.put(type, rootListingForNamespaces);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isValidCachedResourcePath(Path path) {
|
|
||||||
String str = path.toString();
|
|
||||||
for(int i = 0; i < str.length(); i++) {
|
|
||||||
if(!ResourceLocation.validPathChar(str.charAt(i))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "getNamespaces", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void useCacheForNamespaces(PackType type, CallbackInfoReturnable<Set<String>> cir) {
|
|
||||||
if(useNamespaceCaches) {
|
|
||||||
cir.setReturnValue(this.namespacesByType.get(type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "hasResource(Ljava/lang/String;)Z", at = @At(value = "HEAD"), cancellable = true)
|
|
||||||
private void useCacheForExistence(String path, CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
cir.setReturnValue(this.containedPaths.contains(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason Use cached listing of mod resources
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<String> filter)
|
|
||||||
{
|
|
||||||
Path inputPath = this.resourcePackFS.getPath(pathIn);
|
|
||||||
return this.rootListingByNamespaceAndType.get(type).getOrDefault(resourceNamespace, Collections.emptyList()).stream().
|
|
||||||
filter(path -> path.getNameCount() <= maxDepth). // Make sure the depth is within bounds
|
|
||||||
filter(path -> path.startsWith(inputPath)). // Make sure the target path is inside this one
|
|
||||||
filter(path -> filter.test(path.getFileName().toString())). // Test the file name against the predicate
|
|
||||||
// Finally we need to form the RL, so use the first name as the domain, and the rest as the path
|
|
||||||
// It is VERY IMPORTANT that we do not rely on Path.toString as this is inconsistent between operating systems
|
|
||||||
// Join the path names ourselves to force forward slashes
|
|
||||||
map(path -> new ResourceLocation(resourceNamespace, slashJoiner.join(path))).
|
|
||||||
collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.resourcepacks;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import net.minecraft.server.packs.PackType;
|
|
||||||
import net.minecraft.server.packs.VanillaPackResources;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.embeddedt.modernfix.FileWalker;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Mixin(VanillaPackResources.class)
|
|
||||||
public class VanillaPackMixin {
|
|
||||||
@Shadow @Final private static Map<PackType, FileSystem> JAR_FILESYSTEM_BY_TYPE;
|
|
||||||
private static LoadingCache<Pair<Path, Integer>, List<Path>> pathStreamLoadingCache = CacheBuilder.newBuilder()
|
|
||||||
.build(FileWalker.INSTANCE);
|
|
||||||
|
|
||||||
private static Set<String> containedPaths = null;
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
|
||||||
private void cacheContainedPaths(String[] p_i47912_1_, CallbackInfo ci) {
|
|
||||||
if(containedPaths != null)
|
|
||||||
return;
|
|
||||||
containedPaths = new HashSet<>();
|
|
||||||
Joiner slashJoiner = Joiner.on('/');
|
|
||||||
for(PackType type : PackType.values()) {
|
|
||||||
FileSystem fs = JAR_FILESYSTEM_BY_TYPE.get(type);
|
|
||||||
if(fs == null)
|
|
||||||
throw new IllegalStateException("No filesystem for vanilla " + type.name() + " assets");
|
|
||||||
try {
|
|
||||||
Path root = fs.getPath(type.getDirectory()).toAbsolutePath();
|
|
||||||
try(Stream<Path> stream = Files.walk(root)) {
|
|
||||||
stream
|
|
||||||
.map(path -> root.relativize(path.toAbsolutePath()))
|
|
||||||
.forEach(path -> containedPaths.add(slashJoiner.join(type.getDirectory(), path)));
|
|
||||||
}
|
|
||||||
} catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "getResources(Ljava/util/Collection;ILjava/lang/String;Ljava/nio/file/Path;Ljava/lang/String;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;walk(Ljava/nio/file/Path;I[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;"))
|
|
||||||
private static Stream<Path> useCacheForLoading(Path path, int maxDepth, FileVisitOption[] fileVisitOptions) throws IOException {
|
|
||||||
try {
|
|
||||||
return pathStreamLoadingCache.get(Pair.of(path, maxDepth)).stream();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if(e.getCause() instanceof IOException) /* generally always should be */
|
|
||||||
throw (IOException)e.getCause();
|
|
||||||
else
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "hasResource", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResource(Ljava/lang/String;)Ljava/net/URL;"), cancellable = true)
|
|
||||||
private void useCacheForExistence(PackType type, ResourceLocation location, CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + location.getPath()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.skip_first_datapack_reload;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.duck.ILevelSave;
|
|
||||||
import org.embeddedt.modernfix.util.DummyServerConfiguration;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
@Mixin(LevelStorageSource.LevelStorageAccess.class)
|
|
||||||
public class LevelSaveMixin implements ILevelSave {
|
|
||||||
@Shadow @Final private Path levelPath;
|
|
||||||
|
|
||||||
public void runWorldPersistenceHooks(LevelStorageSource format) {
|
|
||||||
((SaveFormatAccessor)format).invokeReadLevelData(this.levelPath.toFile(), (file, dataFixer) -> {
|
|
||||||
try {
|
|
||||||
CompoundTag compoundTag = NbtIo.readCompressed(file);
|
|
||||||
net.minecraftforge.fml.WorldPersistenceHooks.handleWorldDataLoad((LevelStorageSource.LevelStorageAccess)(Object)this, new DummyServerConfiguration(), compoundTag);
|
|
||||||
} catch (Exception e) {
|
|
||||||
ModernFix.LOGGER.error("Exception reading {}", file, e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.skip_first_datapack_reload;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Function4;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
|
||||||
import net.minecraft.world.level.DataPackConfig;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.world.level.storage.WorldData;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClient;
|
|
||||||
import org.embeddedt.modernfix.duck.ILevelSave;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(Minecraft.class)
|
|
||||||
public abstract class MinecraftMixin {
|
|
||||||
@Shadow public abstract Minecraft.ServerStem makeServerStem(RegistryAccess.RegistryHolder dynamicRegistries, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> worldStorageToDatapackFunction, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> quadFunction, boolean vanillaOnly, LevelStorageSource.LevelStorageAccess worldStorage) throws InterruptedException, ExecutionException;
|
|
||||||
|
|
||||||
@Shadow @Final private LevelStorageSource levelSource;
|
|
||||||
|
|
||||||
@Redirect(method = "loadLevel(Ljava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/RegistryAccess;builtin()Lnet/minecraft/core/RegistryAccess$RegistryHolder;"))
|
|
||||||
private RegistryAccess.RegistryHolder useNullRegistry() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "loadWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;makeServerStem(Lnet/minecraft/core/RegistryAccess$RegistryHolder;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)Lnet/minecraft/client/Minecraft$ServerStem;", ordinal = 0))
|
|
||||||
private Minecraft.ServerStem skipFirstReload(Minecraft client, RegistryAccess.RegistryHolder dynamicRegistries, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> worldStorageToDatapackFunction, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> quadFunction, boolean vanillaOnly, LevelStorageSource.LevelStorageAccess levelSave, String worldName, RegistryAccess.RegistryHolder originalRegistries, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> levelSaveToDatapackFunction, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> quadFunction2, boolean vanillaOnly2, Minecraft.ExperimentalDialogType selectionType, boolean creating) throws InterruptedException, ExecutionException {
|
|
||||||
if(!creating) {
|
|
||||||
ModernFix.LOGGER.warn("Skipping first reload, this is still experimental");
|
|
||||||
ModernFix.runningFirstInjection = true;
|
|
||||||
((ILevelSave)levelSave).runWorldPersistenceHooks(levelSource);
|
|
||||||
ModernFix.runningFirstInjection = false;
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
/* allow reload */
|
|
||||||
return makeServerStem(dynamicRegistries, worldStorageToDatapackFunction, quadFunction, vanillaOnly, levelSave);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package org.embeddedt.modernfix.mixin.perf.skip_first_datapack_reload;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
@Mixin(LevelStorageSource.class)
|
|
||||||
public interface SaveFormatAccessor {
|
|
||||||
@Invoker
|
|
||||||
<T> T invokeReadLevelData(File saveDir, BiFunction<File, DataFixer, T> levelDatReader);
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@ package org.embeddedt.modernfix.mixin.perf.thread_priorities;
|
||||||
import com.mojang.authlib.GameProfileRepository;
|
import com.mojang.authlib.GameProfileRepository;
|
||||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.server.ServerResources;
|
import net.minecraft.server.WorldStem;
|
||||||
import net.minecraft.server.packs.repository.PackRepository;
|
import net.minecraft.server.packs.repository.PackRepository;
|
||||||
import net.minecraft.client.server.IntegratedServer;
|
import net.minecraft.client.server.IntegratedServer;
|
||||||
import net.minecraft.server.players.GameProfileCache;
|
import net.minecraft.server.players.GameProfileCache;
|
||||||
|
|
@ -21,9 +21,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
@Mixin(IntegratedServer.class)
|
@Mixin(IntegratedServer.class)
|
||||||
public class IntegratedServerMixin {
|
public class IntegratedServerMixin {
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
private void adjustServerPriority(Thread pServerThread, Minecraft pMinecraft, RegistryAccess.RegistryHolder pRegistryHolder, LevelStorageSource.LevelStorageAccess pStorageSource, PackRepository pPackRepository, ServerResources pResources, WorldData pWorldData, MinecraftSessionService pSessionService, GameProfileRepository pProfileRepository, GameProfileCache pProfileCache, ChunkProgressListenerFactory pProgressListenerfactory, CallbackInfo ci) {
|
private void adjustServerPriority(Thread thread, Minecraft arg, LevelStorageSource.LevelStorageAccess arg2, PackRepository arg3, WorldStem arg4, MinecraftSessionService minecraftSessionService, GameProfileRepository gameProfileRepository, GameProfileCache arg5, ChunkProgressListenerFactory arg6, CallbackInfo ci) {
|
||||||
int pri = ModernFixConfig.INTEGRATED_SERVER_PRIORITY.get();
|
int pri = ModernFixConfig.INTEGRATED_SERVER_PRIORITY.get();
|
||||||
ModernFix.LOGGER.info("Changing server thread priority to " + pri);
|
ModernFix.LOGGER.info("Changing server thread priority to " + pri);
|
||||||
pServerThread.setPriority(pri);
|
thread.setPriority(pri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,11 +89,6 @@ public class LazyBakedModel implements BakedModel {
|
||||||
return computeDelegate().getOverrides();
|
return computeDelegate().getOverrides();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BakedModel getBakedModel() {
|
|
||||||
return computeDelegate().getBakedModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
|
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
|
||||||
|
|
@ -101,8 +96,8 @@ public class LazyBakedModel implements BakedModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAmbientOcclusion(BlockState state) {
|
public boolean useAmbientOcclusion(BlockState state) {
|
||||||
return computeDelegate().isAmbientOcclusion(state);
|
return computeDelegate().useAmbientOcclusion(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -122,8 +117,8 @@ public class LazyBakedModel implements BakedModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TextureAtlasSprite getParticleTexture(@Nonnull IModelData data) {
|
public TextureAtlasSprite getParticleIcon(@Nonnull IModelData data) {
|
||||||
return computeDelegate().getParticleTexture(data);
|
return computeDelegate().getParticleIcon(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
package org.embeddedt.modernfix.searchtree;
|
||||||
|
|
||||||
import mezz.jei.Internal;
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
import mezz.jei.ingredients.IIngredientListElementInfo;
|
import mezz.jei.common.Internal;
|
||||||
import mezz.jei.ingredients.IngredientFilter;
|
import mezz.jei.common.ingredients.IngredientFilter;
|
||||||
import mezz.jei.runtime.JeiRuntime;
|
import mezz.jei.common.runtime.JeiRuntime;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.embeddedt.modernfix.mixin.perf.blast_search_trees.IngredientFilterInvoker;
|
import org.embeddedt.modernfix.mixin.perf.blast_search_trees.IngredientFilterInvoker;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses JEI to handle search tree lookups.
|
* Uses JEI to handle search tree lookups.
|
||||||
|
|
@ -23,9 +24,9 @@ public class JEIBackedSearchTree extends DummySearchTree<ItemStack> {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<ItemStack> search(String pSearchText) {
|
public List<ItemStack> search(String pSearchText) {
|
||||||
JeiRuntime runtime = Internal.getRuntime();
|
Optional<JeiRuntime> runtime = Internal.getRuntime();
|
||||||
if(runtime != null) {
|
if(runtime.isPresent()) {
|
||||||
return this.searchJEI(Internal.getIngredientFilter(), pSearchText);
|
return this.searchJEI((IngredientFilter)runtime.get().getIngredientFilter(), pSearchText);
|
||||||
} else {
|
} else {
|
||||||
/* Use the default, dummy implementation */
|
/* Use the default, dummy implementation */
|
||||||
return super.search(pSearchText);
|
return super.search(pSearchText);
|
||||||
|
|
@ -35,10 +36,10 @@ public class JEIBackedSearchTree extends DummySearchTree<ItemStack> {
|
||||||
private List<ItemStack> searchJEI(IngredientFilter filter, String pSearchText) {
|
private List<ItemStack> searchJEI(IngredientFilter filter, String pSearchText) {
|
||||||
if(!pSearchText.equals(lastSearchText)) {
|
if(!pSearchText.equals(lastSearchText)) {
|
||||||
listCache.clear();
|
listCache.clear();
|
||||||
List<IIngredientListElementInfo<?>> ingredients = ((IngredientFilterInvoker)filter).invokeGetIngredientListUncached(filteringByTag ? ("$" + pSearchText) : pSearchText);
|
List<ITypedIngredient<?>> ingredients = ((IngredientFilterInvoker)filter).invokeGetIngredientListUncached(filteringByTag ? ("$" + pSearchText) : pSearchText);
|
||||||
for(IIngredientListElementInfo<?> ingredient : ingredients) {
|
for(ITypedIngredient<?> ingredient : ingredients) {
|
||||||
if(ingredient.getElement().getIngredient() instanceof ItemStack) {
|
if(ingredient.getIngredient() instanceof ItemStack) {
|
||||||
listCache.add((ItemStack)ingredient.getElement().getIngredient());
|
listCache.add((ItemStack)ingredient.getIngredient());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastSearchText = pSearchText;
|
lastSearchText = pSearchText;
|
||||||
|
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
import net.minecraftforge.fml.common.thread.SidedThreadGroups;
|
|
||||||
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
|
|
||||||
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.sql.Struct;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber(modid = ModernFix.MODID)
|
|
||||||
public class AsyncLocator {
|
|
||||||
private static ExecutorService LOCATING_EXECUTOR_SERVICE = null;
|
|
||||||
private static final AtomicInteger poolNum = new AtomicInteger(1);
|
|
||||||
|
|
||||||
private AsyncLocator() {}
|
|
||||||
|
|
||||||
private static void setupExecutorService() {
|
|
||||||
shutdownExecutorService();
|
|
||||||
|
|
||||||
int threads = 1; // very unlikely we need more than one
|
|
||||||
ModernFix.LOGGER.info("Starting locating executor service with thread pool size of {}", threads);
|
|
||||||
LOCATING_EXECUTOR_SERVICE = Executors.newFixedThreadPool(
|
|
||||||
threads,
|
|
||||||
new ThreadFactory() {
|
|
||||||
|
|
||||||
private final AtomicInteger threadNum = new AtomicInteger(1);
|
|
||||||
private final String namePrefix = "asynclocator-" + poolNum.getAndIncrement() + "-thread-";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Thread newThread(@NotNull Runnable r) {
|
|
||||||
return new Thread(SidedThreadGroups.SERVER, r, namePrefix + threadNum.getAndIncrement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void shutdownExecutorService() {
|
|
||||||
if (LOCATING_EXECUTOR_SERVICE != null) {
|
|
||||||
ModernFix.LOGGER.info("Shutting down locating executor service");
|
|
||||||
LOCATING_EXECUTOR_SERVICE.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void handleServerAboutToStartEvent(FMLServerAboutToStartEvent ignoredEvent) {
|
|
||||||
setupExecutorService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void handleServerStoppingEvent(FMLServerStoppingEvent ignoredEvent) {
|
|
||||||
shutdownExecutorService();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues a task to locate a feature using {@link ServerLevel#findNearestMapFeature(TagKey, BlockPos, int, boolean)}
|
|
||||||
* and returns a {@link LocateTask} with the futures for it.
|
|
||||||
*/
|
|
||||||
public static LocateTask<BlockPos> locateLevel(
|
|
||||||
ServerLevel level,
|
|
||||||
Collection<StructureFeature<?>> structure,
|
|
||||||
BlockPos pos,
|
|
||||||
int searchRadius,
|
|
||||||
boolean skipKnownStructures
|
|
||||||
) {
|
|
||||||
ModernFix.LOGGER.debug(
|
|
||||||
"Creating locate task for {} in {} around {} within {} chunks",
|
|
||||||
structure, level, pos, searchRadius
|
|
||||||
);
|
|
||||||
CompletableFuture<BlockPos> completableFuture = new CompletableFuture<>();
|
|
||||||
Future<?> future = LOCATING_EXECUTOR_SERVICE.submit(
|
|
||||||
() -> doLocateLevel(completableFuture, level, structure, pos, searchRadius, skipKnownStructures)
|
|
||||||
);
|
|
||||||
return new LocateTask<>(level.getServer(), completableFuture, future);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues a task to locate a feature using
|
|
||||||
* {@link ChunkGenerator#findNearestMapFeature(ServerLevel, HolderSet, BlockPos, int, boolean)} and returns a
|
|
||||||
* {@link LocateTask} with the futures for it.
|
|
||||||
*/
|
|
||||||
public static LocateTask<Pair<BlockPos, StructureFeature<?>>> locateChunkGen(
|
|
||||||
ServerLevel level,
|
|
||||||
Collection<StructureFeature<?>> structureSet,
|
|
||||||
BlockPos pos,
|
|
||||||
int searchRadius,
|
|
||||||
boolean skipKnownStructures
|
|
||||||
) {
|
|
||||||
ModernFix.LOGGER.debug(
|
|
||||||
"Creating locate task for {} in {} around {} within {} chunks",
|
|
||||||
structureSet, level, pos, searchRadius
|
|
||||||
);
|
|
||||||
CompletableFuture<Pair<BlockPos, StructureFeature<?>>> completableFuture = new CompletableFuture<>();
|
|
||||||
Future<?> future = LOCATING_EXECUTOR_SERVICE.submit(
|
|
||||||
() -> doLocateChunkGenerator(completableFuture, level, structureSet, pos, searchRadius, skipKnownStructures)
|
|
||||||
);
|
|
||||||
return new LocateTask<>(level.getServer(), completableFuture, future);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String structureSetToString(Collection<StructureFeature<?>> collection) {
|
|
||||||
return "[" + collection.stream().map(StructureFeature::getRegistryName).map(ResourceLocation::toString).collect(Collectors.joining(", ")) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doLocateLevel(
|
|
||||||
CompletableFuture<BlockPos> completableFuture,
|
|
||||||
ServerLevel level,
|
|
||||||
Collection<StructureFeature<?>> structureTag,
|
|
||||||
BlockPos pos,
|
|
||||||
int searchRadius,
|
|
||||||
boolean skipExistingChunks
|
|
||||||
) {
|
|
||||||
String structures = structureSetToString(structureTag);
|
|
||||||
ModernFix.LOGGER.debug(
|
|
||||||
"Trying to locate {} in {} around {} within {} chunks",
|
|
||||||
structures, level, pos, searchRadius
|
|
||||||
);
|
|
||||||
Optional<BlockPos> thePosition = structureTag.stream()
|
|
||||||
.map(tag -> level.findNearestMapFeature(tag, pos, searchRadius, skipExistingChunks))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.findFirst();
|
|
||||||
if (!thePosition.isPresent())
|
|
||||||
ModernFix.LOGGER.debug("No {} found", structures);
|
|
||||||
else
|
|
||||||
ModernFix.LOGGER.debug("Found {} at {}", structures, thePosition.get());
|
|
||||||
completableFuture.complete(thePosition.orElse(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked" })
|
|
||||||
private static void doLocateChunkGenerator(
|
|
||||||
CompletableFuture<Pair<BlockPos, StructureFeature<?>>> completableFuture,
|
|
||||||
ServerLevel level,
|
|
||||||
Collection<StructureFeature<?>> structureSet,
|
|
||||||
BlockPos pos,
|
|
||||||
int searchRadius,
|
|
||||||
boolean skipExistingChunks
|
|
||||||
) {
|
|
||||||
String structures = structureSetToString(structureSet);
|
|
||||||
ModernFix.LOGGER.debug(
|
|
||||||
"Trying to locate {} in {} around {} within {} chunks",
|
|
||||||
structures, level, pos, searchRadius
|
|
||||||
);
|
|
||||||
Optional<Pair<BlockPos, StructureFeature>> foundStructure = structureSet.stream()
|
|
||||||
.map(feature -> Pair.of(level.getChunkSource().getGenerator()
|
|
||||||
.findNearestMapFeature(level, feature, pos, searchRadius, skipExistingChunks), (StructureFeature)feature))
|
|
||||||
.filter(pair -> pair.getFirst() != null)
|
|
||||||
.findFirst();
|
|
||||||
if (!foundStructure.isPresent())
|
|
||||||
ModernFix.LOGGER.debug("No {} found", structures);
|
|
||||||
else
|
|
||||||
ModernFix.LOGGER.debug("Found {} at {}", structures, foundStructure.get().getFirst());
|
|
||||||
completableFuture.complete((Pair<BlockPos, StructureFeature<?>>)(Object)foundStructure.orElse(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holder of the futures for an async locate task as well as providing some helper functions.
|
|
||||||
* The completableFuture will be completed once the call to
|
|
||||||
* {@link ServerLevel#findNearestMapFeature(TagKey, BlockPos, int, boolean)} has completed, and will hold the
|
|
||||||
* result of it.
|
|
||||||
* The taskFuture is the future for the {@link Runnable} itself in the executor service.
|
|
||||||
*/
|
|
||||||
public static class LocateTask<T> {
|
|
||||||
private final MinecraftServer server;
|
|
||||||
private final CompletableFuture<T> completableFuture;
|
|
||||||
private final Future<?> taskFuture;
|
|
||||||
public LocateTask(MinecraftServer server, CompletableFuture<T> completableFuture, Future<?> taskFuture) {
|
|
||||||
this.server = server;
|
|
||||||
this.completableFuture = completableFuture;
|
|
||||||
this.taskFuture = taskFuture;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Helper function that calls {@link CompletableFuture#thenAccept(Consumer)} with the given action.
|
|
||||||
* Bear in mind that the action will be executed from the task's thread. If you intend to change any game data,
|
|
||||||
* it's strongly advised you use {@link #thenOnServerThread(Consumer)} instead so that it's queued and executed
|
|
||||||
* on the main server thread instead.
|
|
||||||
*/
|
|
||||||
public LocateTask<T> then(Consumer<T> action) {
|
|
||||||
completableFuture.thenAccept(action);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function that calls {@link CompletableFuture#thenAccept(Consumer)} with the given action on the server
|
|
||||||
* thread.
|
|
||||||
*/
|
|
||||||
public LocateTask<T> thenOnServerThread(Consumer<T> action) {
|
|
||||||
completableFuture.thenAccept(pos -> server.submit(() -> action.accept(pos)));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function that cancels both completableFuture and taskFuture.
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
taskFuture.cancel(true);
|
|
||||||
completableFuture.cancel(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
||||||
import net.minecraft.world.inventory.ChestMenu;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.Items;
|
|
||||||
import net.minecraft.world.item.MapItem;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapDecoration;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
||||||
import org.embeddedt.modernfix.mixin.perf.async_locator.MapItemAccess;
|
|
||||||
|
|
||||||
public class CommonLogic {
|
|
||||||
private CommonLogic() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty "Filled Map", with a hover tooltip name stating that it's locating a feature.
|
|
||||||
*
|
|
||||||
* @return The ItemStack
|
|
||||||
*/
|
|
||||||
public static ItemStack createEmptyMap() {
|
|
||||||
ItemStack stack = new ItemStack(Items.FILLED_MAP);
|
|
||||||
stack.setHoverName(new TranslatableComponent("asynclocator.map.locating"));
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the map stack with all the given data.
|
|
||||||
*
|
|
||||||
* @param mapStack The map ItemStack to update
|
|
||||||
* @param level The ServerLevel
|
|
||||||
* @param pos The feature position
|
|
||||||
* @param scale The map scale
|
|
||||||
* @param destinationType The map feature type
|
|
||||||
*/
|
|
||||||
public static void updateMap(
|
|
||||||
ItemStack mapStack,
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos pos,
|
|
||||||
int scale,
|
|
||||||
MapDecoration.Type destinationType
|
|
||||||
) {
|
|
||||||
updateMap(mapStack, level, pos, scale, destinationType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the map stack with all the given data.
|
|
||||||
*
|
|
||||||
* @param mapStack The map ItemStack to update
|
|
||||||
* @param level The ServerLevel
|
|
||||||
* @param pos The feature position
|
|
||||||
* @param scale The map scale
|
|
||||||
* @param destinationType The map feature type
|
|
||||||
* @param displayName The hover tooltip display name of the ItemStack
|
|
||||||
*/
|
|
||||||
public static void updateMap(
|
|
||||||
ItemStack mapStack,
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos pos,
|
|
||||||
int scale,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
String displayName
|
|
||||||
) {
|
|
||||||
MapItemAccess.callCreateAndStoreSavedData(
|
|
||||||
mapStack, level, pos.getX(), pos.getZ(), scale, true, true, level.dimension()
|
|
||||||
);
|
|
||||||
MapItem.renderBiomePreviewMap(level, mapStack);
|
|
||||||
MapItemSavedData.addTargetDecoration(mapStack, pos, "+", destinationType);
|
|
||||||
if (displayName != null)
|
|
||||||
mapStack.setHoverName(new TranslatableComponent(displayName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcasts slot changes to all players that have the chest container open.
|
|
||||||
* Won't do anything if the BlockEntity isn't an instance of {@link ChestBlockEntity}.
|
|
||||||
*/
|
|
||||||
public static void broadcastChestChanges(ServerLevel level, BlockEntity be) {
|
|
||||||
if (!(be instanceof ChestBlockEntity))
|
|
||||||
return;
|
|
||||||
|
|
||||||
level.players().forEach(player -> {
|
|
||||||
AbstractContainerMenu container = player.containerMenu;
|
|
||||||
if (container instanceof ChestMenu && ((ChestMenu)container).getContainer() == be) {
|
|
||||||
container.broadcastChanges();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.minecraft.advancements.CriteriaTriggers;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.stats.Stats;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
|
||||||
import net.minecraft.world.item.EnderEyeItem;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import org.embeddedt.modernfix.mixin.perf.async_locator.EyeOfEnderAccess;
|
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
|
|
||||||
public class EnderEyeItemLogic {
|
|
||||||
private EnderEyeItemLogic() {}
|
|
||||||
|
|
||||||
public static void locateAsync(ServerLevel level, Player player, EyeOfEnder eyeOfEnder, EnderEyeItem enderEyeItem) {
|
|
||||||
AsyncLocator.locateChunkGen(
|
|
||||||
level,
|
|
||||||
ImmutableSet.of(StructureFeature.STRONGHOLD),
|
|
||||||
player.blockPosition(),
|
|
||||||
100,
|
|
||||||
false
|
|
||||||
).thenOnServerThread(pos -> {
|
|
||||||
((EyeOfEnderData) eyeOfEnder).setLocateTaskOngoing(false);
|
|
||||||
if (pos != null) {
|
|
||||||
eyeOfEnder.signalTo(pos.getFirst());
|
|
||||||
CriteriaTriggers.USED_ENDER_EYE.trigger((ServerPlayer) player, pos.getFirst());
|
|
||||||
player.awardStat(Stats.ITEM_USED.get(enderEyeItem));
|
|
||||||
} else {
|
|
||||||
// Set the entity's life to long enough that it dies
|
|
||||||
((EyeOfEnderAccess) eyeOfEnder).setLife(Integer.MAX_VALUE - 100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
((EyeOfEnderData) eyeOfEnder).setLocateTaskOngoing(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.item.Item;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.Items;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapDecoration;
|
|
||||||
import net.minecraftforge.fml.loading.Java9BackportUtils;
|
|
||||||
import net.minecraftforge.items.CapabilityItemHandler;
|
|
||||||
import net.minecraftforge.items.IItemHandler;
|
|
||||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
// TODO: Need to test this
|
|
||||||
public class ExplorationMapFunctionLogic {
|
|
||||||
private static final int MAX_STACK_SIZE = 64;
|
|
||||||
private ExplorationMapFunctionLogic() {}
|
|
||||||
|
|
||||||
public static void invalidateMap(ItemStack mapStack, ServerLevel level, BlockPos pos) {
|
|
||||||
handleUpdateMapInChest(mapStack, level, pos, (handler, slot) -> {
|
|
||||||
if (handler instanceof IItemHandlerModifiable) {
|
|
||||||
((IItemHandlerModifiable)handler).setStackInSlot(slot, new ItemStack(Items.MAP));
|
|
||||||
} else {
|
|
||||||
handler.extractItem(slot, MAX_STACK_SIZE, false);
|
|
||||||
handler.insertItem(slot, new ItemStack(Items.MAP), false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateMap(
|
|
||||||
ItemStack mapStack,
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos pos,
|
|
||||||
int scale,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
BlockPos invPos
|
|
||||||
) {
|
|
||||||
CommonLogic.updateMap(mapStack, level, pos, scale, destinationType);
|
|
||||||
// Shouldn't need to set the stack in its slot again, as we're modifying the same instance
|
|
||||||
handleUpdateMapInChest(mapStack, level, invPos, (handler, slot) -> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void handleUpdateMapInChest(
|
|
||||||
ItemStack mapStack,
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos invPos,
|
|
||||||
BiConsumer<IItemHandler, Integer> handleSlotFound
|
|
||||||
) {
|
|
||||||
BlockEntity be = level.getBlockEntity(invPos);
|
|
||||||
if (be != null) {
|
|
||||||
Java9BackportUtils.ifPresentOrElse(be.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve(),
|
|
||||||
itemHandler -> {
|
|
||||||
for (int i = 0; i < itemHandler.getSlots(); i++) {
|
|
||||||
ItemStack slotStack = itemHandler.getStackInSlot(i);
|
|
||||||
if (slotStack == mapStack) {
|
|
||||||
handleSlotFound.accept(itemHandler, i);
|
|
||||||
CommonLogic.broadcastChestChanges(level, be);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() -> ModernFix.LOGGER.warn(
|
|
||||||
"Couldn't find item handler capability on chest {} at {}",
|
|
||||||
be.getClass().getSimpleName(), invPos
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ModernFix.LOGGER.warn(
|
|
||||||
"Couldn't find block entity on chest {} at {}",
|
|
||||||
level.getBlockState(invPos), invPos
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void handleLocationFound(
|
|
||||||
ItemStack mapStack,
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos pos,
|
|
||||||
int scale,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
BlockPos invPos
|
|
||||||
) {
|
|
||||||
if (pos == null) {
|
|
||||||
invalidateMap(mapStack, level, invPos);
|
|
||||||
} else {
|
|
||||||
updateMap(mapStack, level, pos, scale, destinationType, invPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ItemStack updateMapAsync(
|
|
||||||
ServerLevel level,
|
|
||||||
BlockPos blockPos,
|
|
||||||
int scale,
|
|
||||||
int searchRadius,
|
|
||||||
boolean skipKnownStructures,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
StructureFeature<?> destination
|
|
||||||
) {
|
|
||||||
ItemStack mapStack = CommonLogic.createEmptyMap();
|
|
||||||
AsyncLocator.locateLevel(level, ImmutableSet.of(destination), blockPos, searchRadius, skipKnownStructures)
|
|
||||||
.thenOnServerThread(pos -> handleLocationFound(mapStack, level, pos, scale, destinationType, blockPos));
|
|
||||||
return mapStack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
public interface EyeOfEnderData {
|
|
||||||
void setLocateTaskOngoing(boolean locateTaskOngoing);
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.network.chat.TextComponent;
|
|
||||||
import net.minecraft.server.commands.LocateCommand;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import org.embeddedt.modernfix.mixin.perf.async_locator.LocateCommandAccess;
|
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
|
|
||||||
public class LocateCommandLogic {
|
|
||||||
private LocateCommandLogic() {}
|
|
||||||
|
|
||||||
public static void locateAsync(CommandSourceStack sourceStack, StructureFeature<?> feature) {
|
|
||||||
BlockPos originPos = new BlockPos(sourceStack.getPosition());
|
|
||||||
AsyncLocator.locateLevel(sourceStack.getLevel(), ImmutableSet.of(feature), originPos, 100, false)
|
|
||||||
.thenOnServerThread(pair -> {
|
|
||||||
if (pair != null) {
|
|
||||||
LocateCommand.showLocateResult(sourceStack, feature.getFeatureName(), originPos, pair, "commands.locate.success");
|
|
||||||
} else {
|
|
||||||
sourceStack.sendFailure(
|
|
||||||
new TextComponent(
|
|
||||||
LocateCommandAccess.getErrorFailed().create().getMessage()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure.logic;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.entity.npc.AbstractVillager;
|
|
||||||
import net.minecraft.world.entity.npc.Villager;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.Items;
|
|
||||||
import net.minecraft.world.item.trading.MerchantOffer;
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.level.saveddata.maps.MapDecoration;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.mixin.perf.async_locator.MerchantOfferAccess;
|
|
||||||
import org.embeddedt.modernfix.structure.AsyncLocator;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class MerchantLogic {
|
|
||||||
private static final boolean REMOVE_OFFER = false;
|
|
||||||
private MerchantLogic() {}
|
|
||||||
|
|
||||||
public static void invalidateMap(AbstractVillager merchant, ItemStack mapStack) {
|
|
||||||
mapStack.setHoverName(new TranslatableComponent("asynclocator.map.none"));
|
|
||||||
Optional<MerchantOffer> offerOptional = merchant.getOffers()
|
|
||||||
.stream()
|
|
||||||
.filter(offer -> offer.getResult() == mapStack)
|
|
||||||
.findFirst();
|
|
||||||
if(offerOptional.isPresent()) {
|
|
||||||
removeOffer(merchant, offerOptional.get());
|
|
||||||
} else {
|
|
||||||
ModernFix.LOGGER.warn("Failed to find merchant offer for map");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeOffer(AbstractVillager merchant, MerchantOffer offer) {
|
|
||||||
if (REMOVE_OFFER) {
|
|
||||||
merchant.getOffers().remove(offer);
|
|
||||||
} else {
|
|
||||||
((MerchantOfferAccess) offer).setMaxUses(0);
|
|
||||||
offer.setToOutOfStock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void handleLocationFound(
|
|
||||||
ServerLevel level,
|
|
||||||
AbstractVillager merchant,
|
|
||||||
ItemStack mapStack,
|
|
||||||
String displayName,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
BlockPos pos
|
|
||||||
) {
|
|
||||||
if (pos == null) {
|
|
||||||
invalidateMap(merchant, mapStack);
|
|
||||||
} else {
|
|
||||||
CommonLogic.updateMap(mapStack, level, pos, 2, destinationType, displayName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (merchant.getTradingPlayer() instanceof ServerPlayer) {
|
|
||||||
ServerPlayer tradingPlayer = (ServerPlayer)merchant.getTradingPlayer();
|
|
||||||
tradingPlayer.sendMerchantOffers(
|
|
||||||
tradingPlayer.containerMenu.containerId,
|
|
||||||
merchant.getOffers(),
|
|
||||||
merchant instanceof Villager ? ((Villager)merchant).getVillagerData().getLevel() : 1,
|
|
||||||
merchant.getVillagerXp(),
|
|
||||||
merchant.showProgressBar(),
|
|
||||||
merchant.canRestock()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MerchantOffer updateMapAsync(
|
|
||||||
Entity pTrader,
|
|
||||||
int emeraldCost,
|
|
||||||
String displayName,
|
|
||||||
MapDecoration.Type destinationType,
|
|
||||||
int maxUses,
|
|
||||||
int villagerXp,
|
|
||||||
StructureFeature<?> destination
|
|
||||||
) {
|
|
||||||
return updateMapAsyncInternal(
|
|
||||||
pTrader,
|
|
||||||
emeraldCost,
|
|
||||||
maxUses,
|
|
||||||
villagerXp,
|
|
||||||
(level, merchant, mapStack) -> AsyncLocator.locateLevel(level, ImmutableSet.of(destination), merchant.blockPosition(), 100, true)
|
|
||||||
.thenOnServerThread(pos -> handleLocationFound(
|
|
||||||
level,
|
|
||||||
merchant,
|
|
||||||
mapStack,
|
|
||||||
displayName,
|
|
||||||
destinationType,
|
|
||||||
pos
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MerchantOffer updateMapAsyncInternal(
|
|
||||||
Entity trader, int emeraldCost, int maxUses, int villagerXp, MapUpdateTask task
|
|
||||||
) {
|
|
||||||
if (trader instanceof AbstractVillager) {
|
|
||||||
AbstractVillager merchant = (AbstractVillager)trader;
|
|
||||||
ItemStack mapStack = CommonLogic.createEmptyMap();
|
|
||||||
task.apply((ServerLevel) trader.level, merchant, mapStack);
|
|
||||||
|
|
||||||
return new MerchantOffer(
|
|
||||||
new ItemStack(Items.EMERALD, emeraldCost),
|
|
||||||
new ItemStack(Items.COMPASS),
|
|
||||||
mapStack,
|
|
||||||
maxUses,
|
|
||||||
villagerXp,
|
|
||||||
0.2F
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MapUpdateTask {
|
|
||||||
void apply(ServerLevel level, AbstractVillager merchant, ItemStack mapStack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
package org.embeddedt.modernfix.util;
|
|
||||||
|
|
||||||
import com.google.common.base.Stopwatch;
|
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraftforge.fml.ModList;
|
|
||||||
import net.minecraftforge.fml.ModWorkManager;
|
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
|
||||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.objectweb.asm.Type;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class BlockClassPreloader {
|
|
||||||
public static void preloadClasses() {
|
|
||||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
|
||||||
ModernFix.LOGGER.warn("Preparing to preload classes...");
|
|
||||||
HashMap<Type, Boolean> isABlockClass = new HashMap<>();
|
|
||||||
isABlockClass.put(Type.getType(BlockBehaviour.class), true);
|
|
||||||
isABlockClass.put(Type.getType(Block.class), true);
|
|
||||||
Field selfField, parentField;
|
|
||||||
List<CompletableFuture> futures = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
selfField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
|
|
||||||
selfField.setAccessible(true);
|
|
||||||
parentField = ModFileScanData.ClassData.class.getDeclaredField("parent");
|
|
||||||
parentField.setAccessible(true);
|
|
||||||
List<ModFileScanData.ClassData> currentCandidates = ModList.get().getModFiles().stream()
|
|
||||||
.map(ModFileInfo::getFile)
|
|
||||||
.map(ModFile::getScanResult)
|
|
||||||
.flatMap(data -> data.getClasses().stream())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
HashSet<Type> blockClasses = new HashSet<>();
|
|
||||||
blockClasses.add(Type.getType(BlockBehaviour.class));
|
|
||||||
HashSet<Type> nonBlockClasses = new HashSet<>();
|
|
||||||
int previousSize = -1;
|
|
||||||
nonBlockClasses.add(Type.getType(Object.class));
|
|
||||||
currentCandidates.removeIf(clz -> {
|
|
||||||
Type self;
|
|
||||||
try {
|
|
||||||
self = (Type)selfField.get(clz);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return (nonBlockClasses.contains(self) || blockClasses.contains(self));
|
|
||||||
});
|
|
||||||
while(blockClasses.size() > previousSize && currentCandidates.size() > 0) {
|
|
||||||
previousSize = blockClasses.size();
|
|
||||||
currentCandidates.removeIf(clz -> {
|
|
||||||
Type parent, self;
|
|
||||||
try {
|
|
||||||
parent = (Type)parentField.get(clz);
|
|
||||||
self = (Type)selfField.get(clz);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
if(nonBlockClasses.contains(parent)) {
|
|
||||||
nonBlockClasses.add(self);
|
|
||||||
return true;
|
|
||||||
} else if(blockClasses.contains(parent)) {
|
|
||||||
blockClasses.add(self);
|
|
||||||
futures.add(CompletableFuture.runAsync(() -> {
|
|
||||||
if(self.getClassName().toLowerCase(Locale.ROOT).contains("mixin"))
|
|
||||||
return;
|
|
||||||
try {
|
|
||||||
Class.forName(self.getClassName());
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.warn("Couldn't load " + self.getClassName(), e);
|
|
||||||
}
|
|
||||||
}, ModWorkManager.parallelExecutor()));
|
|
||||||
return true;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
futures.forEach(CompletableFuture::join);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
ModernFix.LOGGER.warn("Preloading classes took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
|
||||||
stopwatch.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package org.embeddedt.modernfix.util;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import mezz.jei.Internal;
|
|
||||||
import mezz.jei.api.runtime.IIngredientListOverlay;
|
|
||||||
import mezz.jei.runtime.JeiRuntime;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.gui.Gui;
|
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
|
||||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class JEIUtil {
|
|
||||||
private static Supplier<Boolean> isLoading = null;
|
|
||||||
|
|
||||||
public static void registerLoadingRenderer(Supplier<Boolean> isLoading) {
|
|
||||||
JEIUtil.isLoading = isLoading;
|
|
||||||
MinecraftForge.EVENT_BUS.register(JEIUtil.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void renderLoad(GuiScreenEvent.DrawScreenEvent.Post event) {
|
|
||||||
if(isLoading.get()) {
|
|
||||||
Gui.drawString(new PoseStack(), Minecraft.getInstance().font, new TranslatableComponent("modernfix.jei_load"), 0, 0, 0xffffff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
package org.embeddedt.modernfix.util;
|
package org.embeddedt.modernfix.util;
|
||||||
|
|
||||||
import net.minecraftforge.client.event.ModelBakeEvent;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
import net.minecraftforge.eventbus.EventBus;
|
import net.minecraftforge.eventbus.EventBus;
|
||||||
import net.minecraftforge.eventbus.api.EventListenerHelper;
|
import net.minecraftforge.eventbus.api.EventListenerHelper;
|
||||||
import net.minecraftforge.eventbus.api.IEventListener;
|
|
||||||
import net.minecraftforge.fml.ModContainer;
|
import net.minecraftforge.fml.ModContainer;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
import net.minecraftforge.fml.ModLoader;
|
|
||||||
import net.minecraftforge.fml.ModLoadingContext;
|
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
package org.embeddedt.modernfix.util;
|
package org.embeddedt.modernfix.util;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Stopwatch;
|
|
||||||
import net.minecraftforge.fml.ModContainer;
|
import net.minecraftforge.fml.ModContainer;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
import net.minecraftforge.fml.ModLoadingContext;
|
import net.minecraftforge.fml.ModLoadingContext;
|
||||||
import net.minecraftforge.fml.ModWorkManager;
|
|
||||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||||
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
import net.minecraftforge.forgespi.language.IModInfo;
|
import net.minecraftforge.forgespi.language.IModInfo;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.Marker;
|
import org.apache.logging.log4j.Marker;
|
||||||
import org.apache.logging.log4j.MarkerManager;
|
import org.apache.logging.log4j.MarkerManager;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
|
@ -19,8 +15,6 @@ import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
@ -36,7 +30,7 @@ public class OrderedParallelModDispatcher {
|
||||||
Set<String> finishedMods = Collections.synchronizedSet(new HashSet<>(modIDsToFilter));
|
Set<String> finishedMods = Collections.synchronizedSet(new HashSet<>(modIDsToFilter));
|
||||||
HashMap<String, CompletableFuture<?>> submittedFutures = new HashMap<>();
|
HashMap<String, CompletableFuture<?>> submittedFutures = new HashMap<>();
|
||||||
Semaphore jobWaitingSemaphore = new Semaphore(0);
|
Semaphore jobWaitingSemaphore = new Semaphore(0);
|
||||||
ArrayList<ModInfo> remainingModList = new ArrayList<>(ModList.get().getMods());
|
ArrayList<IModInfo> remainingModList = new ArrayList<>(ModList.get().getMods());
|
||||||
while(remainingModList.size() > 0) {
|
while(remainingModList.size() > 0) {
|
||||||
remainingModList.removeIf(modInfo -> {
|
remainingModList.removeIf(modInfo -> {
|
||||||
if(finishedMods.contains(modInfo.getModId()))
|
if(finishedMods.contains(modInfo.getModId()))
|
||||||
|
|
@ -56,8 +50,7 @@ public class OrderedParallelModDispatcher {
|
||||||
ModContainer container = modContainerOpt.get();
|
ModContainer container = modContainerOpt.get();
|
||||||
ModernFix.LOGGER.debug(DISPATCHER, "Submitting job for " + modInfo.getModId());
|
ModernFix.LOGGER.debug(DISPATCHER, "Submitting job for " + modInfo.getModId());
|
||||||
submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> {
|
submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> {
|
||||||
Supplier<?> contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension");
|
ModLoadingContext.get().setActiveContainer(container);
|
||||||
ModLoadingContext.get().setActiveContainer(container, contextExtension.get());
|
|
||||||
try {
|
try {
|
||||||
task.accept(modInfo.getModId());
|
task.accept(modInfo.getModId());
|
||||||
} catch(RuntimeException e) {
|
} catch(RuntimeException e) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
||||||
modLoader = "javafml" #mandatory
|
modLoader = "javafml" #mandatory
|
||||||
# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
|
# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
|
||||||
loaderVersion = "[36,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
|
loaderVersion = "[40,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
|
||||||
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
||||||
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
||||||
license = "GNU LGPL 3.0"
|
license = "GNU LGPL 3.0"
|
||||||
|
|
@ -43,7 +43,7 @@ modId = "forge" #mandatory
|
||||||
# Does this dependency have to exist - if not, ordering below must be specified
|
# Does this dependency have to exist - if not, ordering below must be specified
|
||||||
mandatory = true #mandatory
|
mandatory = true #mandatory
|
||||||
# The version range of the dependency
|
# The version range of the dependency
|
||||||
versionRange = "[36,)" #mandatory
|
versionRange = "[40,)" #mandatory
|
||||||
# An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory
|
# An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory
|
||||||
ordering = "NONE"
|
ordering = "NONE"
|
||||||
# Side this dependency is applied on - BOTH, CLIENT or SERVER
|
# Side this dependency is applied on - BOTH, CLIENT or SERVER
|
||||||
|
|
@ -53,13 +53,13 @@ side = "BOTH"
|
||||||
modId = "minecraft"
|
modId = "minecraft"
|
||||||
mandatory = true
|
mandatory = true
|
||||||
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
||||||
versionRange = "[1.16.5,1.17)"
|
versionRange = "[1.18.2,1.19)"
|
||||||
ordering = "NONE"
|
ordering = "NONE"
|
||||||
side = "BOTH"
|
side = "BOTH"
|
||||||
[[dependencies.modernfix]]
|
[[dependencies.modernfix]]
|
||||||
modId = "jei"
|
modId = "jei"
|
||||||
mandatory = false
|
mandatory = false
|
||||||
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
||||||
versionRange = "[7.7.1.153,)"
|
versionRange = "[10,)"
|
||||||
ordering = "BEFORE"
|
ordering = "BEFORE"
|
||||||
side = "CLIENT"
|
side = "CLIENT"
|
||||||
|
|
|
||||||
|
|
@ -3,49 +3,27 @@
|
||||||
"minVersion": "0.8",
|
"minVersion": "0.8",
|
||||||
"package": "org.embeddedt.modernfix.mixin",
|
"package": "org.embeddedt.modernfix.mixin",
|
||||||
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
|
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
|
||||||
"compatibilityLevel": "JAVA_8",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"refmap": "modernfix.refmap.json",
|
"refmap": "modernfix.refmap.json",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
|
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
|
||||||
"bugfix.structure_manager_crash.StructureManagerMixin",
|
|
||||||
"perf.remove_biome_temperature_cache.BiomeMixin",
|
"perf.remove_biome_temperature_cache.BiomeMixin",
|
||||||
"perf.resourcepacks.ModFileResourcePackMixin",
|
|
||||||
"perf.resourcepacks.VanillaPackMixin",
|
|
||||||
"perf.skip_first_datapack_reload.LevelSaveMixin",
|
|
||||||
"perf.skip_first_datapack_reload.SaveFormatAccessor",
|
|
||||||
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
|
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
|
||||||
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
||||||
"perf.boost_worker_count.UtilMixin",
|
|
||||||
"perf.thread_priorities.UtilMixin",
|
"perf.thread_priorities.UtilMixin",
|
||||||
"perf.preload_block_classes.GameDataMixin",
|
|
||||||
"perf.reduce_blockstate_cache_rebuilds.BlocksMixin",
|
"perf.reduce_blockstate_cache_rebuilds.BlocksMixin",
|
||||||
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
||||||
"perf.reduce_blockstate_cache_rebuilds.ShapeCacheMixin",
|
"perf.reduce_blockstate_cache_rebuilds.ShapeCacheMixin",
|
||||||
"perf.deduplicate_location.MixinResourceLocation",
|
"perf.deduplicate_location.MixinResourceLocation",
|
||||||
"perf.sync_executor_sleep.SyncExecutorMixin",
|
"perf.sync_executor_sleep.SyncExecutorMixin",
|
||||||
"perf.compress_biome_container.MixinBiomeContainer",
|
|
||||||
"perf.nuke_empty_chunk_sections.MixinChunk",
|
|
||||||
"perf.cache_blockstate_cache_arrays.AbstractBlockStateCacheMixin",
|
"perf.cache_blockstate_cache_arrays.AbstractBlockStateCacheMixin",
|
||||||
"perf.datapack_reload_exceptions.LootTableManagerMixin",
|
"perf.datapack_reload_exceptions.LootTableManagerMixin",
|
||||||
"perf.datapack_reload_exceptions.RecipeManagerMixin",
|
"perf.datapack_reload_exceptions.RecipeManagerMixin",
|
||||||
"perf.async_locator.CommandSourceStackAccess",
|
|
||||||
"perf.async_locator.DolphinSwimToTreasureGoalMixin",
|
|
||||||
"perf.async_locator.EnderEyeItemMixin",
|
|
||||||
"perf.async_locator.ExplorationMapFunctionMixin",
|
|
||||||
"perf.async_locator.EyeOfEnderAccess",
|
|
||||||
"perf.async_locator.EyeOfEnderMixin",
|
|
||||||
"perf.async_locator.LocateCommandAccess",
|
|
||||||
"perf.async_locator.LocateCommandMixin",
|
|
||||||
"perf.async_locator.MapItemAccess",
|
|
||||||
"perf.async_locator.MerchantOfferAccess",
|
|
||||||
"perf.async_locator.TreasureMapForEmeraldsMixin",
|
|
||||||
"feature.measure_time.BootstrapMixin"
|
"feature.measure_time.BootstrapMixin"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"feature.measure_time.MinecraftMixin",
|
"feature.measure_time.MinecraftMixin",
|
||||||
"feature.reduce_loading_screen_freezes.ModelBakeryMixin",
|
"feature.reduce_loading_screen_freezes.ModelBakeryMixin",
|
||||||
"perf.skip_first_datapack_reload.MinecraftMixin",
|
|
||||||
"bugfix.concurrency.RenderTypeMixin",
|
|
||||||
"bugfix.concurrency.MinecraftMixin",
|
"bugfix.concurrency.MinecraftMixin",
|
||||||
"perf.parallelize_model_loading.ModelBakeryMixin",
|
"perf.parallelize_model_loading.ModelBakeryMixin",
|
||||||
"perf.parallelize_model_loading.OBJLoaderMixin",
|
"perf.parallelize_model_loading.OBJLoaderMixin",
|
||||||
|
|
@ -55,11 +33,6 @@
|
||||||
"perf.parallelize_model_loading.TransformationMatrixMixin",
|
"perf.parallelize_model_loading.TransformationMatrixMixin",
|
||||||
"perf.parallelize_model_loading.BooleanPropertyMixin",
|
"perf.parallelize_model_loading.BooleanPropertyMixin",
|
||||||
"perf.parallelize_model_loading.PropertyMixin",
|
"perf.parallelize_model_loading.PropertyMixin",
|
||||||
"perf.async_jei.IngredientListElementFactoryMixin",
|
|
||||||
"perf.async_jei.ClientLifecycleHandlerMixin",
|
|
||||||
"perf.async_jei.JeiStarterMixin",
|
|
||||||
"perf.async_jei.PluginCallerMixin",
|
|
||||||
"perf.async_jei.RecipeManagerInternalMixin",
|
|
||||||
"perf.thread_priorities.IntegratedServerMixin",
|
"perf.thread_priorities.IntegratedServerMixin",
|
||||||
"safety.BlockColorsMixin",
|
"safety.BlockColorsMixin",
|
||||||
"perf.flatten_model_predicates.AndConditionMixin",
|
"perf.flatten_model_predicates.AndConditionMixin",
|
||||||
|
|
@ -71,9 +44,7 @@
|
||||||
"perf.faster_baking.BlockModelShapesMixin",
|
"perf.faster_baking.BlockModelShapesMixin",
|
||||||
"perf.faster_baking.ModelManagerMixin",
|
"perf.faster_baking.ModelManagerMixin",
|
||||||
"perf.cache_model_materials.VanillaModelMixin",
|
"perf.cache_model_materials.VanillaModelMixin",
|
||||||
"perf.cache_model_materials.MultipartMixin",
|
"perf.cache_model_materials.MultipartMixin"
|
||||||
"bugfix.packet_leak.ClientPlayNetHandlerMixin",
|
|
||||||
"bugfix.packet_leak.SCustomPayloadPlayPacketMixin"
|
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user