Improve class transformer cache

This commit is contained in:
embeddedt 2023-01-22 14:23:06 -05:00
parent 95d7410722
commit 23b4652864
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
10 changed files with 105 additions and 148 deletions

View File

@ -4,8 +4,12 @@ import java.io.*;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import cpw.mods.modlauncher.api.ITransformer; import cpw.mods.modlauncher.api.ITransformer;
@ -44,6 +48,14 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 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);
}
});
private static File childFile(File file) { private static File childFile(File file) {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
return file; return file;
@ -82,12 +94,12 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
} }
} }
private ArrayList<byte[]> computeHash(String className, boolean isEmpty, String reason) { private ArrayList<byte[]> computeHash(String className, byte[] inputClass, String reason) {
final String internalName = className.replace('.', '/'); final String internalName = className.replace('.', '/');
final Type classDesc = Type.getObjectType(internalName); final Type classDesc = Type.getObjectType(internalName);
ArrayList<ILaunchPluginService> pluginList = new ArrayList<>(); ArrayList<ILaunchPluginService> pluginList = new ArrayList<>();
for(ILaunchPluginService plugin : plugins.values()) { for(ILaunchPluginService plugin : plugins.values()) {
if(!plugin.handlesClass(classDesc, isEmpty, reason).isEmpty()) { if(!plugin.handlesClass(classDesc, inputClass.length == 0, reason).isEmpty()) {
pluginList.add(plugin); pluginList.add(plugin);
} }
} }
@ -117,6 +129,10 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
} }
} }
} }
/* 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; return hashList;
} }
@ -132,42 +148,52 @@ public class ModernFixCachingClassTransformer extends ClassTransformer {
public byte[] transform(byte[] inputClass, String className, String reason) { public byte[] transform(byte[] inputClass, String className, String reason) {
/* We only want to cache actual transformations */ /* We only want to cache actual transformations */
if(ITransformerActivity.CLASSLOADING_REASON.equals(reason)) { if(ITransformerActivity.CLASSLOADING_REASON.equals(reason)) {
ArrayList<byte[]> hashList = computeHash(className, inputClass.length == 0, reason); final byte[] classToHash = inputClass;
if(hashList != null) { CompletableFuture<ArrayList<byte[]>> futureHashList = CompletableFuture.supplyAsync(() -> computeHash(className, classToHash, reason), ForkJoinPool.commonPool());
/* Check if the cache contains a transformed class matching these hashes */ ArrayList<byte[]> hashList = null;
/* TODO maybe sanitize the class name? */ /* Check if the cache contains a transformed class matching these hashes */
File cacheLocation = new File(CLASS_CACHE_FOLDER, className.replace('.', '/')); /* TODO maybe sanitize the class name? */
boolean hashesMatch = true; File cacheLocation = new File(CLASS_CACHE_FOLDER, className.replace('.', '/'));
try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(cacheLocation))) { boolean hashesMatch = true;
ArrayList<byte[]> savedHash = (ArrayList<byte[]>)stream.readObject(); try(ObjectInputStream stream = new ObjectInputStream(new FileInputStream(cacheLocation))) {
ArrayList<byte[]> savedHash = (ArrayList<byte[]>)stream.readObject();
byte[] savedInputClass = (byte[])stream.readObject();
hashList = futureHashList.get();
if(hashList != null) {
for(int i = 0; i < savedHash.size(); i++) { for(int i = 0; i < savedHash.size(); i++) {
if(!Arrays.equals(savedHash.get(i), hashList.get(i))) { if(!Arrays.equals(savedHash.get(i), hashList.get(i))) {
hashesMatch = false; hashesMatch = false;
break; break;
} }
} }
if(hashesMatch) } else
inputClass = (byte[])stream.readObject();
} catch(IOException | ClassNotFoundException | ClassCastException e) {
if(!(e instanceof FileNotFoundException))
e.printStackTrace();
hashesMatch = false; hashesMatch = false;
} if(hashesMatch)
if(!hashesMatch) { inputClass = savedInputClass;
inputClass = super.transform(inputClass, className, reason); } catch(IOException | ClassNotFoundException | ClassCastException | InterruptedException |
ExecutionException 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 byte[] classToSave = inputClass;
final ArrayList<byte[]> hashListToSave = hashList;
classSaverPool.submit(() -> { classSaverPool.submit(() -> {
cacheLocation.getParentFile().mkdirs(); cacheLocation.getParentFile().mkdirs();
try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(cacheLocation))) { try(ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(cacheLocation))) {
stream.writeObject(hashList); stream.writeObject(hashListToSave);
stream.writeObject(classToSave); stream.writeObject(classToSave);
} catch(IOException e) { } catch(IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
}); });
} }
return inputClass;
} }
return inputClass;
} }
return super.transform(inputClass, className, reason); return super.transform(inputClass, className, reason);
} }

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.classloading.hashers; package org.embeddedt.modernfix.classloading.hashers;
import cpw.mods.modlauncher.ModernFixCachingClassTransformer;
import net.minecraftforge.coremod.CoreMod; import net.minecraftforge.coremod.CoreMod;
import net.minecraftforge.coremod.transformer.CoreModBaseTransformer; import net.minecraftforge.coremod.transformer.CoreModBaseTransformer;
@ -13,13 +14,6 @@ import java.util.concurrent.ConcurrentHashMap;
public class CoreModTransformerHasher { public class CoreModTransformerHasher {
private static final ConcurrentHashMap<CoreMod, byte[]> hashForCoremod; private static final ConcurrentHashMap<CoreMod, byte[]> hashForCoremod;
private static Field coremodField; private static Field coremodField;
private static ThreadLocal<MessageDigest> coremodHasher = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("SHA-256");
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
});
static { static {
hashForCoremod = new ConcurrentHashMap<>(); hashForCoremod = new ConcurrentHashMap<>();
@ -38,10 +32,9 @@ public class CoreModTransformerHasher {
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
MessageDigest hasher = coremodHasher.get(); MessageDigest hasher = ModernFixCachingClassTransformer.systemHasher.get();
byte[] hash = hasher.digest(coreModContents);
hasher.reset(); hasher.reset();
return hash; return hasher.digest(coreModContents);
} }
public static byte[] obtainHash(CoreModBaseTransformer<?> transformer) { public static byte[] obtainHash(CoreModBaseTransformer<?> transformer) {

View File

@ -1,12 +1,11 @@
package org.embeddedt.modernfix.classloading.hashers; package org.embeddedt.modernfix.classloading.hashers;
import com.google.common.collect.HashMultimap;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import cpw.mods.modlauncher.ModernFixCachingClassTransformer; import cpw.mods.modlauncher.ModernFixCachingClassTransformer;
import org.spongepowered.asm.launch.IClassProcessor; import org.spongepowered.asm.launch.IClassProcessor;
import org.spongepowered.asm.launch.MixinLaunchPluginLegacy; import org.spongepowered.asm.launch.MixinLaunchPluginLegacy;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.service.MixinService;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -19,6 +18,10 @@ public class MixinTransformerHasher {
private static HashMap<String, byte[]> hashesByClass = null; private static HashMap<String, byte[]> hashesByClass = null;
private final static MessageDigest hasher; private final static MessageDigest hasher;
private static Field processorsListField, transformerField, processorField, environmentField;
private static final byte[] NO_MIXINS = new byte[] {(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef};
static { static {
try { try {
hasher = MessageDigest.getInstance("SHA-256"); hasher = MessageDigest.getInstance("SHA-256");
@ -31,12 +34,12 @@ public class MixinTransformerHasher {
/* FIXME runs too early right now, and therefore doesn't pick up the list of mixins correctly */ /* FIXME runs too early right now, and therefore doesn't pick up the list of mixins correctly */
synchronized (MixinTransformerHasher.class) { synchronized (MixinTransformerHasher.class) {
if(hashesByClass == null) { if(hashesByClass == null) {
hashesByClass = new HashMap<>();
HashMap<String, ArrayList<IMixinInfo>> mixinsByClass = new HashMap<>();
try { try {
Field processorsField = MixinLaunchPluginLegacy.class.getDeclaredField("processors"); if(processorsListField == null) {
processorsField.setAccessible(true); processorsListField = MixinLaunchPluginLegacy.class.getDeclaredField("processors");
List<IClassProcessor> processors = (List<IClassProcessor>)processorsField.get(plugin); processorsListField.setAccessible(true);
}
List<IClassProcessor> processors = (List<IClassProcessor>) processorsListField.get(plugin);
Object transformHandler = null; Object transformHandler = null;
for(IClassProcessor processor : processors) { for(IClassProcessor processor : processors) {
if(processor.getClass().getName().equals("org.spongepowered.asm.service.modlauncher.MixinTransformationHandler")) { if(processor.getClass().getName().equals("org.spongepowered.asm.service.modlauncher.MixinTransformationHandler")) {
@ -46,12 +49,24 @@ public class MixinTransformerHasher {
} }
if(transformHandler == null) if(transformHandler == null)
throw new IllegalStateException("Mixin transform handler not found"); throw new IllegalStateException("Mixin transform handler not found");
Field transformerField = transformHandler.getClass().getDeclaredField("transformer"); if(transformerField == null) {
transformerField.setAccessible(true); transformerField = transformHandler.getClass().getDeclaredField("transformer");
transformerField.setAccessible(true);
}
Object transformer = transformerField.get(transformHandler); Object transformer = transformerField.get(transformHandler);
Field processorField = transformer.getClass().getDeclaredField("processor"); if(processorField == null) {
processorField.setAccessible(true); processorField = transformer.getClass().getDeclaredField("processor");
processorField.setAccessible(true);
}
Object processor = processorField.get(transformer); 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"); Field configsField = processor.getClass().getDeclaredField("configs");
configsField.setAccessible(true); configsField.setAccessible(true);
List<?> configs = (List<?>)configsField.get(processor); List<?> configs = (List<?>)configsField.get(processor);
@ -60,6 +75,7 @@ public class MixinTransformerHasher {
/* getTargetClasses can't be used because it's package-private */ /* getTargetClasses can't be used because it's package-private */
Field classNamesField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo").getDeclaredField("targetClassNames"); Field classNamesField = Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo").getDeclaredField("targetClassNames");
classNamesField.setAccessible(true); classNamesField.setAccessible(true);
HashMap<String, ArrayList<IMixinInfo>> mixinsByClass = new HashMap<>();
for(Object config : configs) { for(Object config : configs) {
List<? extends IMixinInfo> mixins = (List<? extends IMixinInfo>)mixinsField.get(config); List<? extends IMixinInfo> mixins = (List<? extends IMixinInfo>)mixinsField.get(config);
for(IMixinInfo mixin : mixins) { for(IMixinInfo mixin : mixins) {
@ -73,6 +89,7 @@ public class MixinTransformerHasher {
infos.sort((info1, info2) -> Comparator.<String>naturalOrder().compare(info1.getClassName(), info2.getClassName())); infos.sort((info1, info2) -> Comparator.<String>naturalOrder().compare(info1.getClassName(), info2.getClassName()));
} }
/* Now go through each class name and hash it */ /* Now go through each class name and hash it */
HashMap<String, byte[]> hashesByClassInit = new HashMap<>();
for(Map.Entry<String, ArrayList<IMixinInfo>> mixinsForClass : mixinsByClass.entrySet()) { for(Map.Entry<String, ArrayList<IMixinInfo>> mixinsForClass : mixinsByClass.entrySet()) {
hasher.reset(); hasher.reset();
for(IMixinInfo mixin : mixinsForClass.getValue()) { for(IMixinInfo mixin : mixinsForClass.getValue()) {
@ -87,13 +104,14 @@ public class MixinTransformerHasher {
} }
hasher.update(bytecode); hasher.update(bytecode);
} }
hashesByClass.put(mixinsForClass.getKey().replace('/', '.'), hasher.digest()); hashesByClassInit.put(mixinsForClass.getKey().replace('/', '.'), hasher.digest());
} }
hashesByClass = hashesByClassInit;
} catch(ReflectiveOperationException e) { } catch(ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }
return hashesByClass.getOrDefault(className, ModernFixCachingClassTransformer.EMPTY_BYTE_ARRAY); return hashesByClass.getOrDefault(className, NO_MIXINS);
} }
} }

View File

@ -59,23 +59,6 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
throw new IllegalStateException("Expected a TransformingClassLoader"); throw new IllegalStateException("Expected a TransformingClassLoader");
} }
try { try {
if(isOptionEnabled("launch.transformer_cache.ModernFixClassTransformer")) {
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.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");
}
if(isOptionEnabled("launch.class_search_cache.ModernFixResourceFinder")) { if(isOptionEnabled("launch.class_search_cache.ModernFixResourceFinder")) {
Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder"); Field resourceFinderField = TransformingClassLoader.class.getDeclaredField("resourceFinder");
/* Construct a new list of resource finders, using similar logic to ML */ /* Construct a new list of resource finders, using similar logic to ML */
@ -88,7 +71,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
resourceFinder = EnumerationHelper.mergeFunctors(resourceFinder, LamdbaExceptionUtils.rethrowFunction(dcl::findResources)); resourceFinder = EnumerationHelper.mergeFunctors(resourceFinder, LamdbaExceptionUtils.rethrowFunction(dcl::findResources));
resourceFinderField.set(loader, resourceFinder); resourceFinderField.set(loader, resourceFinder);
} }
} catch(RuntimeException | ReflectiveOperationException | IOException e) { } catch(RuntimeException | ReflectiveOperationException e) {
logger.error("Failed to make classloading changes", e); logger.error("Failed to make classloading changes", e);
} }
} }
@ -127,7 +110,28 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
@Override @Override
public void onLoad(String mixinPackage) { 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.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

View File

@ -30,7 +30,7 @@ public class ModernFixEarlyConfig {
this.addMixinRule("perf.async_jei", 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.preload_block_classes", false);
this.addMixinRule("perf.parallel_potentially_unsafe", false); this.addMixinRule("perf.defer_voxelshape_optimize", false);
this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true); this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true);
this.addMixinRule("perf.deduplicate_location", true); this.addMixinRule("perf.deduplicate_location", true);
this.addMixinRule("safety", true); this.addMixinRule("safety", true);

View File

@ -1,27 +0,0 @@
package org.embeddedt.modernfix.mixin.perf.parallel_potentially_unsafe.parallel_deferred_suppliers;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistry;
import org.embeddedt.modernfix.registry.DeferredRegisterBaker;
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.ModifyArg;
import java.util.function.Supplier;
@Mixin(DeferredRegister.class)
public class DeferredRegisterMixin {
@Shadow(remap = false) private IForgeRegistry type;
@Shadow(remap = false) @Final private String modid;
@ModifyArg(method = "register(Ljava/lang/String;Ljava/util/function/Supplier;)Lnet/minecraftforge/fml/RegistryObject;", at = @At(value = "INVOKE", target = "Ljava/util/Map;putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"), index = 1, remap = false)
private Object swapForCachedSupplier(Object original) {
if(this.type != null)
return DeferredRegisterBaker.cacheForComputationLater(this.type.getRegistryName(), this.modid, (Supplier<?>)original);
else
return original;
}
}

View File

@ -1,24 +0,0 @@
package org.embeddedt.modernfix.mixin.perf.parallel_potentially_unsafe.parallel_deferred_suppliers;
import net.minecraft.block.DispenserBlock;
import net.minecraft.dispenser.IDispenseItemBehavior;
import net.minecraft.item.Item;
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.Map;
import java.util.concurrent.ConcurrentHashMap;
@Mixin(DispenserBlock.class)
public class DispenserBlockMixin {
@Shadow private static Map<Item, IDispenseItemBehavior> DISPENSER_REGISTRY;
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void makeMapConcurrent(CallbackInfo ci) {
DISPENSER_REGISTRY = new ConcurrentHashMap<>();
}
}

View File

@ -1,31 +0,0 @@
package org.embeddedt.modernfix.mixin.perf.parallel_potentially_unsafe.parallel_deferred_suppliers;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistry;
import org.embeddedt.modernfix.registry.DeferredRegisterBaker;
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;
import java.util.HashMap;
@Mixin(DeferredRegister.EventDispatcher.class)
public class EventDispatcherMixin {
private static HashMap<ResourceLocation, Boolean> hasRegistryBaked = new HashMap<>();
@Inject(method = "handleEvent", at = @At("HEAD"), remap = false)
private void bakeIfNeeded(RegistryEvent.Register<?> event, CallbackInfo ci) {
IForgeRegistry<?> registry = event.getRegistry();
if(registry == null)
return;
ResourceLocation location = registry.getRegistryName();
if(location == null || !(location.getNamespace().equals("minecraft") && location.getPath().equals("block")))
return;
if(!hasRegistryBaked.getOrDefault(location, false)) {
DeferredRegisterBaker.bakeSuppliers(location);
hasRegistryBaked.put(location, true);
}
}
}

View File

@ -2,3 +2,4 @@ public net.minecraft.client.Minecraft$WorldSelectionType
public net.minecraft.client.renderer.RenderType$Type public net.minecraft.client.renderer.RenderType$Type
public net.minecraft.client.renderer.RenderType$Type <init>(Ljava/lang/String;Lnet/minecraft/client/renderer/vertex/VertexFormat;IIZZLnet/minecraft/client/renderer/RenderType$State;)V public net.minecraft.client.renderer.RenderType$Type <init>(Ljava/lang/String;Lnet/minecraft/client/renderer/vertex/VertexFormat;IIZZLnet/minecraft/client/renderer/RenderType$State;)V
public net.minecraft.block.AbstractBlock$AbstractBlockState$Cache public net.minecraft.block.AbstractBlock$AbstractBlockState$Cache
public net.minecraft.util.math.shapes.VoxelShape <init>(Lnet/minecraft/util/math/shapes/VoxelShapePart;)V # <init>

View File

@ -19,12 +19,9 @@
"perf.boost_worker_count.UtilMixin", "perf.boost_worker_count.UtilMixin",
"perf.thread_priorities.UtilMixin", "perf.thread_priorities.UtilMixin",
"perf.preload_block_classes.GameDataMixin", "perf.preload_block_classes.GameDataMixin",
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.DeferredRegisterMixin",
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.EventDispatcherMixin",
"perf.parallel_blockstate_cache_rebuild.BlocksMixin", "perf.parallel_blockstate_cache_rebuild.BlocksMixin",
"perf.parallel_blockstate_cache_rebuild.BlockCallbacksMixin", "perf.parallel_blockstate_cache_rebuild.BlockCallbacksMixin",
"perf.parallel_blockstate_cache_rebuild.ShapeCacheMixin", "perf.parallel_blockstate_cache_rebuild.ShapeCacheMixin",
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.DispenserBlockMixin",
"perf.deduplicate_location.MixinResourceLocation" "perf.deduplicate_location.MixinResourceLocation"
], ],
"client": [ "client": [