Remove a lot of now dead code
This commit is contained in:
parent
4c36f04f56
commit
863fbaf3a4
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,19 +20,15 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||
import org.embeddedt.modernfix.load.LoadEvents;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
|
||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Ref;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -49,9 +45,6 @@ public class ModernFixClient {
|
|||
public ModernFixClient() {
|
||||
// clear reserve as it's not needed
|
||||
ObfuscationReflectionHelper.setPrivateValue(Minecraft.class, null, new byte[0], "field_71444_a");
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
|
||||
MinecraftForge.EVENT_BUS.register(new LoadEvents());
|
||||
}
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||
Optional<? extends ModContainer> mfContainer = ModList.get().getModContainerById("modernfix");
|
||||
if(mfContainer.isPresent())
|
||||
|
|
@ -91,7 +84,6 @@ public class ModernFixClient {
|
|||
if(event.phase == TickEvent.Phase.END
|
||||
&& recipesUpdated
|
||||
&& tagsUpdated
|
||||
&& !(Minecraft.getInstance().screen instanceof DeferredLevelLoadingScreen)
|
||||
&& worldLoadStartTime != -1
|
||||
&& Minecraft.getInstance().player != null
|
||||
&& numRenderTicks++ >= 10) {
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
package org.embeddedt.modernfix.agent;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Agent {
|
||||
public static void agentmain(String args, Instrumentation instrumentation) {
|
||||
instrumentation.addTransformer(new EarlyTransformer());
|
||||
}
|
||||
|
||||
private static class EarlyTransformer implements ClassFileTransformer {
|
||||
|
||||
private static final ImmutableMap<String, Function<ClassNode, ClassNode>> TRANSFORMERS = ImmutableMap.<String, Function<ClassNode, ClassNode>>builder()
|
||||
.put("net/minecraftforge/fml/loading/moddiscovery/Scanner", EarlyTransformer::transformScanner)
|
||||
.build();
|
||||
|
||||
private static ClassNode transformScanner(ClassNode input) {
|
||||
for(MethodNode method : input.methods) {
|
||||
if(method.name.equals("fileVisitor")) {
|
||||
for(int i = 0; i < method.instructions.size(); i++) {
|
||||
AbstractInsnNode ainsn = method.instructions.get(i);
|
||||
if(ainsn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
MethodInsnNode minsn = (MethodInsnNode)ainsn;
|
||||
if(minsn.name.equals("accept") && minsn.owner.equals("org/objectweb/asm/ClassReader")) {
|
||||
method.instructions.set(minsn.getPrevious(), new LdcInsnNode(ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES));
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
|
||||
Function<ClassNode, ClassNode> func = TRANSFORMERS.get(s);
|
||||
if(func != null) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
ClassNode node = new ClassNode(Opcodes.ASM9);
|
||||
reader.accept(node, 0);
|
||||
node = func.apply(node);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
node.accept(writer);
|
||||
return writer.toByteArray();
|
||||
} else
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -134,29 +134,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
|||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ public class ModernFixEarlyConfig {
|
|||
/* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */
|
||||
this.addMixinRule("perf.blast_search_trees", modPresent("jei"));
|
||||
this.addMixinRule("safety", true);
|
||||
this.addMixinRule("launch.transformer_cache", false);
|
||||
this.addMixinRule("launch.class_search_cache", true);
|
||||
boolean isDevEnv = !FMLLoader.isProduction() && FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getLocator() instanceof ExplodedDirectoryLocator;
|
||||
this.addMixinRule("devenv", isDevEnv);
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
package org.embeddedt.modernfix.load;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.LevelLoadingScreen;
|
||||
import net.minecraft.client.gui.screens.ProgressScreen;
|
||||
import net.minecraft.client.server.IntegratedServer;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.ForcedChunksSavedData;
|
||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
||||
import net.minecraftforge.common.world.ForgeChunkManager;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
|
||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/**
|
||||
* Handles deferring the world load screen.
|
||||
* <p></p>
|
||||
* TODO: The vanilla check that at least 441 chunks have been loaded does not check whether they are spawn chunks
|
||||
* or chunks loaded by the player. Consequently it is possible for loading to finish before every spawn chunk has
|
||||
* been loaded. However the chunk system has at least been warmed up by this point so the remaining chunks load
|
||||
* reasonably quickly.
|
||||
*/
|
||||
public class LoadEvents {
|
||||
private boolean hasFirstPlayerJoined = false;
|
||||
|
||||
@SubscribeEvent
|
||||
public void serverWillStart(FMLServerAboutToStartEvent event) {
|
||||
hasFirstPlayerJoined = false;
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
if(!hasFirstPlayerJoined && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
|
||||
hasFirstPlayerJoined = true;
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
if(server instanceof IntegratedServer) {
|
||||
handleInitialChunkLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onWorldShow(GuiOpenEvent event) {
|
||||
if(ServerLifecycleHooks.getCurrentServer() instanceof IntegratedServer) {
|
||||
if(event.getGui() == null && Minecraft.getInstance().level != null && integratedWorldLoadListener != null) {
|
||||
/* this means the world is about to be displayed, check if 441 initialized */
|
||||
ServerChunkCache provider = ServerLifecycleHooks.getCurrentServer().overworld().getChunkSource();
|
||||
BooleanSupplier worldLoadDone = () -> provider.getTickingGenerated() >= 441;
|
||||
if(!worldLoadDone.getAsBoolean()) {
|
||||
DeferredLevelLoadingScreen newScreen = new DeferredLevelLoadingScreen(Minecraft.getInstance().progressListener.get(), worldLoadDone);
|
||||
event.setGui(newScreen);
|
||||
}
|
||||
} else if(event.getGui() instanceof LevelLoadingScreen && Minecraft.getInstance().level == null && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
|
||||
ProgressScreen loadscreen = new ProgressScreen();
|
||||
loadscreen.progressStartNoAbort(new TranslatableComponent("connect.joining"));
|
||||
event.setGui(loadscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ChunkProgressListener integratedWorldLoadListener;
|
||||
|
||||
private void handleInitialChunkLoad() {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
ServerLevel overworld = server.overworld();
|
||||
ServerChunkCache provider = overworld.getChunkSource();
|
||||
provider.getLightEngine().setTaskPerBatch(500);
|
||||
provider.addRegionTicket(TicketType.START, new ChunkPos(overworld.getSharedSpawnPos()), 11, Unit.INSTANCE);
|
||||
while(provider.getTickingGenerated() < 441) {
|
||||
server.runAllTasks();
|
||||
Thread.yield();
|
||||
LockSupport.parkNanos("waiting for world load", 100000L);
|
||||
server.nextTickTime = Util.getMillis() + 10;
|
||||
}
|
||||
for(ServerLevel serverworld1 : server.getAllLevels()) {
|
||||
ForcedChunksSavedData forcedchunkssavedata = serverworld1.getDataStorage().get(ForcedChunksSavedData::new, "chunks");
|
||||
if (forcedchunkssavedata != null) {
|
||||
LongIterator longiterator = forcedchunkssavedata.getChunks().iterator();
|
||||
|
||||
while(longiterator.hasNext()) {
|
||||
long i = longiterator.nextLong();
|
||||
ChunkPos chunkpos = new ChunkPos(i);
|
||||
serverworld1.getChunkSource().updateChunkForced(chunkpos, true);
|
||||
}
|
||||
|
||||
ForgeChunkManager.reinstatePersistentChunks(serverworld1, forcedchunkssavedata);
|
||||
}
|
||||
}
|
||||
server.runAllTasks();
|
||||
server.nextTickTime = Util.getMillis() + 10;
|
||||
provider.getLightEngine().setTaskPerBatch(5);
|
||||
if(integratedWorldLoadListener != null) {
|
||||
integratedWorldLoadListener.stop();
|
||||
integratedWorldLoadListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.faster_singleplayer_load;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.server.IntegratedServer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.load.LoadEvents;
|
||||
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(MinecraftServer.class)
|
||||
public abstract class MinecraftServerMixin {
|
||||
@Shadow protected long nextTickTime;
|
||||
|
||||
@Shadow public abstract ServerLevel overworld();
|
||||
|
||||
@Shadow protected abstract void updateMobSpawningFlags();
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason defer the 441 chunk load until *after* join game packets are sent to the client, in order to allow
|
||||
* mods that process advancements, etc. to work on that at the same time
|
||||
*/
|
||||
@Inject(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getChunkSource()Lnet/minecraft/server/level/ServerChunkCache;", ordinal = 0), cancellable = true)
|
||||
private void skipInitialChunkLoad(ChunkProgressListener arg, CallbackInfo ci) {
|
||||
if(((Object)this) instanceof IntegratedServer) {
|
||||
ci.cancel();
|
||||
LoadEvents.integratedWorldLoadListener = arg;
|
||||
this.nextTickTime = Util.getMillis();
|
||||
this.overworld().getChunkSource().getLightEngine().setTaskPerBatch(5);
|
||||
this.updateMobSpawningFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.block.model.multipart.AndCondition;
|
||||
import net.minecraft.client.renderer.block.model.multipart.Condition;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
|
||||
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 java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(AndCondition.class)
|
||||
public class AndConditionMixin {
|
||||
@Shadow @Final private Iterable<? extends Condition> conditions;
|
||||
|
||||
/**
|
||||
* @author JellySquid
|
||||
* @reason Flatten predicates
|
||||
*/
|
||||
@Overwrite
|
||||
public Predicate<BlockState> getPredicate(StateDefinition<Block, BlockState> stateManager) {
|
||||
return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
|
||||
return multipartModelSelector.getPredicate(stateManager);
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.block.model.multipart.Condition;
|
||||
import net.minecraft.client.renderer.block.model.multipart.OrCondition;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
|
||||
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 java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(OrCondition.class)
|
||||
public class OrConditionMixin {
|
||||
@Shadow @Final private Iterable<? extends Condition> conditions;
|
||||
|
||||
/**
|
||||
* @author JellySquid
|
||||
* @reason Flatten predicates
|
||||
*/
|
||||
@Overwrite
|
||||
public Predicate<BlockState> getPredicate(StateDefinition<Block, BlockState> stateManager) {
|
||||
return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
|
||||
return multipartModelSelector.getPredicate(stateManager);
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.block.model.multipart.KeyValueCondition;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
|
||||
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 java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(KeyValueCondition.class)
|
||||
public class PropertyValueConditionMixin {
|
||||
@Shadow @Final private String key;
|
||||
|
||||
@Shadow @Final private String value;
|
||||
|
||||
@Shadow @Final private static Splitter PIPE_SPLITTER;
|
||||
|
||||
/**
|
||||
* @author JellySquid
|
||||
* @reason De-duplication
|
||||
*/
|
||||
@Overwrite
|
||||
public Predicate<BlockState> getPredicate(StateDefinition<Block, BlockState> stateManager) {
|
||||
Property<?> property = stateManager.getProperty(this.key);
|
||||
|
||||
if (property == null) {
|
||||
throw new RuntimeException(String.format("Unknown property '%s' on '%s'", this.key, stateManager.getOwner().toString()));
|
||||
}
|
||||
|
||||
String valueString = this.value;
|
||||
boolean negate = !valueString.isEmpty() && valueString.charAt(0) == '!';
|
||||
|
||||
if (negate) {
|
||||
valueString = valueString.substring(1);
|
||||
}
|
||||
|
||||
List<String> split = PIPE_SPLITTER.splitToList(valueString);
|
||||
|
||||
if (split.isEmpty()) {
|
||||
throw new RuntimeException(String.format("Empty value '%s' for property '%s' on '%s'", this.value, this.key, stateManager.getOwner().toString()));
|
||||
}
|
||||
|
||||
Predicate<BlockState> predicate;
|
||||
|
||||
if (split.size() == 1) {
|
||||
predicate = new SingleMatchOne(property, this.getPropertyValue(stateManager, property, valueString));
|
||||
} else {
|
||||
predicate = SingleMatchAny.create(property, split.stream()
|
||||
.map(str -> this.getPropertyValue(stateManager, property, str))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return negate ? predicate.negate() : predicate;
|
||||
}
|
||||
|
||||
private Object getPropertyValue(StateDefinition<Block, BlockState> stateFactory, Property<?> property, String valueString) {
|
||||
Object value = property.getValue(valueString)
|
||||
.orElse(null);
|
||||
|
||||
if (value == null) {
|
||||
throw new RuntimeException(String.format("Unknown value '%s' for property '%s' on '%s' in '%s'",
|
||||
valueString, this.key, stateFactory.getOwner().toString(), this.value));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AllPredicate<T> implements Predicate<T> {
|
||||
private final Predicate<T>[] predicates;
|
||||
|
||||
public AllPredicate(Predicate<T>[] predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> predicate : this.predicates) {
|
||||
if (!predicate.test(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AnyPredicate<T> implements Predicate<T> {
|
||||
private final Predicate<T>[] predicates;
|
||||
|
||||
public AnyPredicate(Predicate<T>[] predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> predicate : this.predicates) {
|
||||
if (predicate.test(t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
/**
|
||||
* Calculates the
|
||||
*/
|
||||
public class CachedModelPredicate {
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.predicate.all.AllMatchOneBoolean;
|
||||
import org.embeddedt.modernfix.predicate.all.AllMatchOneObject;
|
||||
import org.embeddedt.modernfix.predicate.any.AllMatchAnyObject;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class StatePropertyPredicateHelper {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Predicate<BlockState> allMatch(List<Predicate<BlockState>> predicates) {
|
||||
if (SingleMatchOne.areOfType(predicates)) {
|
||||
if (SingleMatchOne.valuesMatchType(predicates, Boolean.class)) {
|
||||
return new AllMatchOneBoolean(predicates);
|
||||
}
|
||||
|
||||
return new AllMatchOneObject(predicates);
|
||||
} else if (SingleMatchAny.areOfType(predicates)) {
|
||||
return new AllMatchAnyObject(predicates);
|
||||
}
|
||||
|
||||
return new AllPredicate<>(predicates.toArray(new Predicate[0]));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Predicate<BlockState> anyMatch(List<Predicate<BlockState>> predicates) {
|
||||
return new AnyPredicate<>(predicates.toArray(new Predicate[0]));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.all;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AllMatchOneBoolean implements Predicate<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final boolean[] values;
|
||||
|
||||
public AllMatchOneBoolean(List<Predicate<BlockState>> list) {
|
||||
int size = list.size();
|
||||
|
||||
this.properties = new Property[size];
|
||||
this.values = new boolean[size];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
SingleMatchOne predicate = (SingleMatchOne) list.get(i);
|
||||
|
||||
this.properties[i] = predicate.property;
|
||||
this.values[i] = (boolean) predicate.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canReplace(List<Predicate<BlockState>> list) {
|
||||
return list.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
for (int i = 0; i < this.properties.length; i++) {
|
||||
Boolean value = (Boolean) blockState.getValue(this.properties[i]);
|
||||
|
||||
if (value != this.values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.all;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AllMatchOneObject implements Predicate<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final Object[] values;
|
||||
|
||||
public AllMatchOneObject(List<Predicate<BlockState>> list) {
|
||||
int size = list.size();
|
||||
|
||||
this.properties = new Property[size];
|
||||
this.values = new Object[size];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
SingleMatchOne predicate = (SingleMatchOne) list.get(i);
|
||||
|
||||
this.properties[i] = predicate.property;
|
||||
this.values[i] = predicate.value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
for (int i = 0; i < this.properties.length; i++) {
|
||||
if (blockState.getValue(this.properties[i]) != this.values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.any;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AllMatchAnyObject implements Predicate<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final Object[][] values;
|
||||
|
||||
public AllMatchAnyObject(List<Predicate<BlockState>> list) {
|
||||
int size = list.size();
|
||||
|
||||
this.properties = new Property[size];
|
||||
this.values = new Object[size][];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
SingleMatchAny predicate = (SingleMatchAny) list.get(i);
|
||||
|
||||
this.properties[i] = predicate.property;
|
||||
this.values[i] = predicate.values;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
for (int i = 0; i < this.properties.length; i++) {
|
||||
if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.single;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SingleMatchAny implements Predicate<BlockState> {
|
||||
public static final ObjectOpenHashSet<SingleMatchAny> PREDICATES = new ObjectOpenHashSet<>();
|
||||
|
||||
public final Property<?> property;
|
||||
public final Object[] values;
|
||||
|
||||
private SingleMatchAny(Property<?> property, List<Object> values) {
|
||||
this.property = property;
|
||||
this.values = values.toArray();
|
||||
}
|
||||
|
||||
public static SingleMatchAny create(Property<?> property, List<Object> values) {
|
||||
return PREDICATES.addOrGet(new SingleMatchAny(property, values));
|
||||
}
|
||||
|
||||
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchAny;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchAny &&
|
||||
Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
return ArrayUtils.contains(this.values, blockState.getValue(this.property));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SingleMatchAny that = (SingleMatchAny) o;
|
||||
return Objects.equals(property, that.property) &&
|
||||
Arrays.equals(values, that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(property);
|
||||
result = 31 * result + Arrays.hashCode(values);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.single;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SingleMatchOne implements Predicate<BlockState> {
|
||||
public final Property<?> property;
|
||||
public final Object value;
|
||||
|
||||
public SingleMatchOne(Property<?> property, Object value) {
|
||||
this.property = property;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchOne;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
return blockState.getValue(this.property) == this.value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModWorkManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.util.AsyncStopwatch;
|
||||
import org.embeddedt.modernfix.util.CachedSupplier;
|
||||
import org.embeddedt.modernfix.util.OrderedParallelModDispatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DeferredRegisterBaker {
|
||||
private static final HashMap<ResourceLocation, HashMap<String, List<CachedSupplier<?>>>> supplierMap = new HashMap<>();
|
||||
public static <T> Supplier<T> cacheForComputationLater(ResourceLocation registry, String modid, Supplier<T> supplier) {
|
||||
synchronized (supplierMap) {
|
||||
HashMap<String, List<CachedSupplier<?>>> registrySupplierMap = supplierMap.computeIfAbsent(registry, reg -> new HashMap<>());
|
||||
List<CachedSupplier<?>> modSupplierList = registrySupplierMap.computeIfAbsent(modid, id -> new ArrayList<>());
|
||||
CachedSupplier<T> cacher = new CachedSupplier<>(supplier);
|
||||
modSupplierList.add(cacher);
|
||||
return cacher;
|
||||
}
|
||||
}
|
||||
|
||||
public static void bakeSuppliers(ResourceLocation registry) {
|
||||
synchronized (supplierMap) {
|
||||
Set<String> modErrors = Collections.synchronizedSet(new HashSet<>());
|
||||
HashMap<String, List<CachedSupplier<?>>> registrySupplierMap = supplierMap.get(registry);
|
||||
if(registrySupplierMap == null)
|
||||
return;
|
||||
ModernFix.LOGGER.info("Caching suppliers for " + registry);
|
||||
Stopwatch realtimeStopwatch = Stopwatch.createStarted();
|
||||
AsyncStopwatch cpuStopwatch = new AsyncStopwatch();
|
||||
OrderedParallelModDispatcher.dispatchBlocking(ModWorkManager.parallelExecutor(), modId -> {
|
||||
List<CachedSupplier<?>> suppliersToCompute = registrySupplierMap.get(modId);
|
||||
if (suppliersToCompute == null || suppliersToCompute.size() == 0) {
|
||||
return;
|
||||
}
|
||||
cpuStopwatch.startMeasuringAsync();
|
||||
for (CachedSupplier<?> supplier : suppliersToCompute) {
|
||||
try {
|
||||
supplier.compute();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.debug("Exception encountered while caching supplier", e);
|
||||
modErrors.add(modId);
|
||||
}
|
||||
}
|
||||
cpuStopwatch.stopMeasuringAsync();
|
||||
});
|
||||
realtimeStopwatch.stop();
|
||||
if(modErrors.size() > 0)
|
||||
ModernFix.LOGGER.warn("The following mods had errors while caching " + registry + " suppliers (this is likely safe): [" + String.join(", ", modErrors) + "]");
|
||||
ModernFix.LOGGER.info("CPU time spent constructing " + registry + " suppliers: " + cpuStopwatch.getCpuTime()/1000f + " seconds");
|
||||
ModernFix.LOGGER.info("Real time spent constructing " + registry + " suppliers: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package org.embeddedt.modernfix.screen;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.screens.LevelLoadingScreen;
|
||||
import net.minecraft.server.level.progress.StoringChunkProgressListener;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public class DeferredLevelLoadingScreen extends LevelLoadingScreen {
|
||||
private final BooleanSupplier worldLoadFinished;
|
||||
public DeferredLevelLoadingScreen(StoringChunkProgressListener arg, BooleanSupplier worldLoadFinished) {
|
||||
super(arg);
|
||||
this.worldLoadFinished = worldLoadFinished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if(this.worldLoadFinished.getAsBoolean())
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBackground(PoseStack matrixStack, int vOffset) {
|
||||
renderDirtBackground(vOffset);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class AsyncStopwatch {
|
||||
private final AtomicLong cpuTimeMs = new AtomicLong(0);
|
||||
private final ThreadLocal<Stopwatch> threadStopwatch = ThreadLocal.withInitial(Stopwatch::createUnstarted);
|
||||
|
||||
public void startMeasuringAsync() {
|
||||
threadStopwatch.get().start();
|
||||
}
|
||||
|
||||
public void stopMeasuringAsync() {
|
||||
Stopwatch watch = threadStopwatch.get();
|
||||
watch.stop();
|
||||
long elapsed = watch.elapsed(TimeUnit.MILLISECONDS);
|
||||
cpuTimeMs.addAndGet(elapsed);
|
||||
watch.reset();
|
||||
}
|
||||
|
||||
public void ensureStoppedAsync() {
|
||||
Stopwatch watch = threadStopwatch.get();
|
||||
if(watch.isRunning())
|
||||
stopMeasuringAsync();
|
||||
}
|
||||
|
||||
public long getCpuTime() {
|
||||
return cpuTimeMs.get();
|
||||
}
|
||||
|
||||
public static void measureAndLogSerialRunningTime(String label, Runnable runnable) {
|
||||
ModernFix.LOGGER.info(label + "...");
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
try {
|
||||
runnable.run();
|
||||
ModernFix.LOGGER.info(label + " took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
} finally {
|
||||
stopwatch.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,33 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An implementation of Supplier that allows separating the time at which the value is computed from when it is
|
||||
* retrieved.
|
||||
*/
|
||||
public class CachedSupplier<T> implements Supplier<T> {
|
||||
private T value = null;
|
||||
|
||||
private boolean hasBeenComputed;
|
||||
private final Supplier<T> delegate;
|
||||
|
||||
public CachedSupplier(Supplier<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public synchronized void compute() {
|
||||
this.value = this.delegate.get();
|
||||
this.hasBeenComputed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get() {
|
||||
if(this.hasBeenComputed) {
|
||||
this.hasBeenComputed = false;
|
||||
return this.value;
|
||||
} else {
|
||||
return this.delegate.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import net.minecraft.network.chat.TranslatableComponent;
|
|||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -25,7 +24,7 @@ public class JEIUtil {
|
|||
@SubscribeEvent
|
||||
public static void renderLoad(GuiScreenEvent.DrawScreenEvent.Post event) {
|
||||
/* Don't show the JEI indicator on the level loading screen, that looks weird */
|
||||
if(isLoading.get() && !(event.getGui() instanceof DeferredLevelLoadingScreen)) {
|
||||
if(isLoading.get()) {
|
||||
Gui.drawString(new PoseStack(), Minecraft.getInstance().font, new TranslatableComponent("modernfix.jei_load"), 0, 0, 0xffffff);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
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.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.MarkerManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
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.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Iterates over all mods in the game, parallelizing where possible while preserving dependency ordering.
|
||||
*
|
||||
* Can also be given a list of mods to skip.
|
||||
*/
|
||||
public class OrderedParallelModDispatcher {
|
||||
private static final Marker DISPATCHER = MarkerManager.getMarker("OrderedParallelModDispatcher");
|
||||
public static void dispatchBlocking(Executor executor, Consumer<String> task, Collection<String> modIDsToFilter) {
|
||||
Set<String> finishedMods = Collections.synchronizedSet(new HashSet<>(modIDsToFilter));
|
||||
HashMap<String, CompletableFuture<?>> submittedFutures = new HashMap<>();
|
||||
Semaphore jobWaitingSemaphore = new Semaphore(0);
|
||||
ArrayList<ModInfo> remainingModList = new ArrayList<>(ModList.get().getMods());
|
||||
while(remainingModList.size() > 0) {
|
||||
remainingModList.removeIf(modInfo -> {
|
||||
if(finishedMods.contains(modInfo.getModId()))
|
||||
return true;
|
||||
List<String> missingDependencies = modInfo.getDependencies().stream()
|
||||
.filter(IModInfo.ModVersion::isMandatory)
|
||||
.map(IModInfo.ModVersion::getModId)
|
||||
.filter(modId -> !finishedMods.contains(modId))
|
||||
.collect(Collectors.toList());
|
||||
if(missingDependencies.size() > 0) {
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Cannot process " + modInfo.getModId() + ", as it is waiting on mods: [" + String.join(", ", missingDependencies) + "]");
|
||||
return false;
|
||||
}
|
||||
Optional<? extends ModContainer> modContainerOpt = ModList.get().getModContainerById(modInfo.getModId());
|
||||
if(!modContainerOpt.isPresent())
|
||||
throw new IllegalStateException("Can't find mod container");
|
||||
ModContainer container = modContainerOpt.get();
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Submitting job for " + modInfo.getModId());
|
||||
submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> {
|
||||
Supplier<?> contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension");
|
||||
ModLoadingContext.get().setActiveContainer(container, contextExtension.get());
|
||||
try {
|
||||
task.accept(modInfo.getModId());
|
||||
} catch(RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/*
|
||||
* We cannot rely on the main thread to correctly mark us as done, as it might start running
|
||||
* before the future is marked as complete. So we add the mod to the finished set ourselves.
|
||||
*/
|
||||
finishedMods.add(modInfo.getModId());
|
||||
jobWaitingSemaphore.release();
|
||||
//ModLoadingContext.get().setActiveContainer(null, null);
|
||||
}, executor));
|
||||
return true;
|
||||
});
|
||||
Preconditions.checkState(submittedFutures.size() > 0, "The semaphore will block forever!");
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Waiting for one of [" + String.join(", ", submittedFutures.keySet()) + "] to finish...");
|
||||
try {
|
||||
jobWaitingSemaphore.acquire();
|
||||
} catch(InterruptedException e) {
|
||||
throw new RuntimeException("Unexpected interruption", e);
|
||||
}
|
||||
submittedFutures.entrySet().removeIf(entry -> {
|
||||
if(entry.getValue().isDone()) {
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Job finished for " + entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
submittedFutures.values().forEach(CompletableFuture::join);
|
||||
}
|
||||
|
||||
public static void dispatchBlocking(Executor executor, Consumer<String> task) {
|
||||
dispatchBlocking(executor, task, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
"perf.skip_first_datapack_reload.SaveFormatAccessor",
|
||||
"perf.boost_worker_count.UtilMixin",
|
||||
"perf.thread_priorities.UtilMixin",
|
||||
"perf.preload_block_classes.GameDataMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.BlocksMixin",
|
||||
|
|
@ -104,9 +103,6 @@
|
|||
"perf.thread_priorities.IntegratedServerMixin",
|
||||
"safety.BlockColorsMixin",
|
||||
"safety.ItemColorsMixin",
|
||||
"perf.flatten_model_predicates.AndConditionMixin",
|
||||
"perf.flatten_model_predicates.OrConditionMixin",
|
||||
"perf.flatten_model_predicates.PropertyValueConditionMixin",
|
||||
"perf.blast_search_trees.MinecraftMixin",
|
||||
"perf.blast_search_trees.IngredientFilterInvoker",
|
||||
"perf.cache_model_materials.VanillaModelMixin",
|
||||
|
|
@ -121,7 +117,6 @@
|
|||
"perf.use_integrated_resources.jepb.PiglinBarteringRecipeBuilderMixin",
|
||||
"perf.jeresources_startup.VillagerEntryMixin",
|
||||
"bugfix.mc218112.SynchedEntityDataMixin_Client",
|
||||
"perf.faster_singleplayer_load.MinecraftServerMixin",
|
||||
"devenv.MinecraftMixin",
|
||||
"devenv.NarratorMixin"
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user