Merge remote-tracking branch 'origin/1.18' into 1.19.2
This commit is contained in:
commit
d63b5acd6e
|
|
@ -1,26 +1,37 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.event.server.ServerStoppedEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.*;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLConfig;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.network.NetworkConstants;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.modernfix.classloading.ModFileScanDataDeduplicator;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||
import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler;
|
||||
import org.embeddedt.modernfix.packet.PacketHandler;
|
||||
import org.embeddedt.modernfix.registry.ObjectHolderClearer;
|
||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
|
@ -66,12 +77,14 @@ public class ModernFix {
|
|||
// Register ourselves for server and other game events we are interested in
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onLoadComplete);
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> MinecraftForge.EVENT_BUS.register(new ModernFixClient()));
|
||||
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(EntityDataIDSyncHandler.class);
|
||||
PacketHandler.register();
|
||||
ModFileScanDataDeduplicator.deduplicate();
|
||||
}
|
||||
|
||||
private static boolean dfuModPresent() {
|
||||
|
|
@ -100,5 +113,32 @@ public class ModernFix {
|
|||
float gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
ModernFix.LOGGER.warn("Dedicated server took " + gameStartTime + " seconds to load");
|
||||
}
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onLoadComplete(FMLLoadCompleteEvent event) {
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onServerDead(ServerStoppedEvent event) {
|
||||
/* Clear as much data from the integrated server as possible, in case a mod holds on to it */
|
||||
try {
|
||||
Field updatingMapField = ObfuscationReflectionHelper.findField(ChunkMap.class, "f_140129_");
|
||||
Field visibleMapField = ObfuscationReflectionHelper.findField(ChunkMap.class, "f_140130_");
|
||||
Field pendingUnloadsField = ObfuscationReflectionHelper.findField(ChunkMap.class, "f_140131_");
|
||||
for(ServerLevel level : event.getServer().getAllLevels()) {
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
Long2ObjectMap<ChunkHolder> map = (Long2ObjectMap<ChunkHolder>)updatingMapField.get(chunkMap);
|
||||
map.clear();
|
||||
map = (Long2ObjectMap<ChunkHolder>)visibleMapField.get(chunkMap);
|
||||
map.clear();
|
||||
map = (Long2ObjectMap<ChunkHolder>)pendingUnloadsField.get(chunkMap);
|
||||
map.clear();
|
||||
}
|
||||
} catch(RuntimeException | IllegalAccessException e) {
|
||||
ModernFix.LOGGER.error("Couldn't clear chunk data", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
package org.embeddedt.modernfix.classloading;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ModFileScanDataDeduplicator {
|
||||
private final Interner<Type> typeInterner = Interners.newStrongInterner();
|
||||
|
||||
private final Function<Type, Type> internerFn = type -> type != null ? typeInterner.intern(type) : null;
|
||||
|
||||
private static Field classClazzField, parentField, interfacesField, annotationClazzField, annotationTypeField;
|
||||
private static final boolean reflectionSuccessful;
|
||||
|
||||
static {
|
||||
boolean success = false;
|
||||
try {
|
||||
classClazzField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
|
||||
classClazzField.setAccessible(true);
|
||||
parentField = ModFileScanData.ClassData.class.getDeclaredField("parent");
|
||||
parentField.setAccessible(true);
|
||||
interfacesField = ModFileScanData.ClassData.class.getDeclaredField("interfaces");
|
||||
interfacesField.setAccessible(true);
|
||||
annotationClazzField = ModFileScanData.AnnotationData.class.getDeclaredField("clazz");
|
||||
annotationClazzField.setAccessible(true);
|
||||
annotationTypeField = ModFileScanData.AnnotationData.class.getDeclaredField("annotationType");
|
||||
annotationTypeField.setAccessible(true);
|
||||
success = true;
|
||||
} catch(ReflectiveOperationException | RuntimeException e) {
|
||||
}
|
||||
reflectionSuccessful = success;
|
||||
}
|
||||
|
||||
ModFileScanDataDeduplicator() {
|
||||
}
|
||||
|
||||
private void runDeduplication() {
|
||||
ModList.get().forEachModFile(this::deduplicateFile);
|
||||
}
|
||||
|
||||
private void deduplicateFile(IModFile file) {
|
||||
ModFileScanData data = file.getScanResult();
|
||||
if(data != null) {
|
||||
data.getClasses().forEach(this::deduplicateClass);
|
||||
data.getAnnotations().forEach(this::deduplicateAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
private void deduplicateClass(ModFileScanData.ClassData data) {
|
||||
try {
|
||||
Type type = (Type)classClazzField.get(data);
|
||||
type = internerFn.apply(type);
|
||||
classClazzField.set(data, type);
|
||||
type = (Type)parentField.get(data);
|
||||
type = internerFn.apply(type);
|
||||
parentField.set(data, type);
|
||||
Set<Type> types = (Set<Type>)interfacesField.get(data);
|
||||
types = types.stream().map(internerFn).collect(Collectors.toSet());
|
||||
interfacesField.set(data, types);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void deduplicateAnnotation(ModFileScanData.AnnotationData data) {
|
||||
try {
|
||||
Type type = (Type)annotationClazzField.get(data);
|
||||
type = internerFn.apply(type);
|
||||
annotationClazzField.set(data, type);
|
||||
type = (Type)annotationTypeField.get(data);
|
||||
type = internerFn.apply(type);
|
||||
annotationTypeField.set(data, type);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void deduplicate() {
|
||||
if(!reflectionSuccessful)
|
||||
return;
|
||||
new ModFileScanDataDeduplicator().runDeduplication();
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ public class ModernFixEarlyConfig {
|
|||
/* Use a simpler ArrayMap if FerriteCore is using the map intelligently anyway */
|
||||
this.addMixinRule("perf.state_definition_construct", modPresent("ferritecore"));
|
||||
this.addMixinRule("perf.cache_strongholds", true);
|
||||
this.addMixinRule("perf.dedup_blockstate_flattening_map", false);
|
||||
this.addMixinRule("perf.clear_mixin_classinfo", false);
|
||||
this.addMixinRule("perf.cache_upgraded_structures", true);
|
||||
this.addMixinRule("perf.compress_blockstate", false);
|
||||
this.addMixinRule("bugfix.concurrency", true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockFaceUV;
|
||||
|
||||
public class UVController {
|
||||
public static final ThreadLocal<Boolean> useDummyUv = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
public static final BlockFaceUV dummyUv = new BlockFaceUV(new float[4], 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dedup_blockstate_flattening_map;
|
||||
|
||||
import net.minecraft.util.datafix.fixes.BlockStateData;
|
||||
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(BlockStateData.class)
|
||||
public class BlockStateDataMixin {
|
||||
@Inject(method = {"register", "finalizeMaps"}, at = @At("HEAD"), cancellable = true)
|
||||
private static void noFlattening(CallbackInfo ci) {
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = {"upgradeBlockStateTag", "upgradeBlock(I)Ljava/lang/String;", "upgradeBlock(Ljava/lang/String;)Ljava/lang/String;", "getTag"}, at = @At("HEAD"), require = 4)
|
||||
private static void preventCorruption(CallbackInfoReturnable<?> cir) {
|
||||
throw new UnsupportedOperationException("Performing the Flattening is currently disabled in the ModernFix config.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dedup_blockstate_flattening_map;
|
||||
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(ChunkPalettedStorageFix.class)
|
||||
public class ChunkPalettedStorageFixMixin {
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/DataFixUtils;make(Ljava/lang/Object;Ljava/util/function/Consumer;)Ljava/lang/Object;"))
|
||||
private static Object skipMakingMap(Object o, Consumer<?> consumer) {
|
||||
return o;
|
||||
}
|
||||
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/datafix/fixes/BlockStateData;getTag(I)Lcom/mojang/serialization/Dynamic;"))
|
||||
private static Dynamic<?> getFakeAirTag(int id) {
|
||||
return new Dynamic<>(NbtOps.INSTANCE, new CompoundTag());
|
||||
}
|
||||
|
||||
@Inject(method = "fix", at = @At("HEAD"))
|
||||
private void skipFix(CallbackInfoReturnable<Dynamic<?>> cir) {
|
||||
throw new UnsupportedOperationException("No Flattening for you.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||
import org.embeddedt.modernfix.dynamicresources.UVController;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@Mixin(BlockElementFace.Deserializer.class)
|
||||
public class BlockElementFaceDeserializerMixin {
|
||||
|
||||
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0))
|
||||
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
||||
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,10 +38,7 @@ import net.minecraftforge.registries.ForgeRegistries;
|
|||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.embeddedt.modernfix.dynamicresources.ResourcePackHandler;
|
||||
import org.embeddedt.modernfix.dynamicresources.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
|
@ -291,10 +288,12 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
}
|
||||
modelFiles.clear();
|
||||
CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join();
|
||||
UVController.useDummyUv.set(Boolean.TRUE);
|
||||
for(CompletableFuture<Pair<ResourceLocation, JsonElement>> future : modelBytes) {
|
||||
Pair<ResourceLocation, JsonElement> pair = future.join();
|
||||
try {
|
||||
if(pair.getSecond() != null) {
|
||||
|
||||
BlockModel model = ExtendedBlockModelDeserializer.INSTANCE.fromJson(pair.getSecond(), BlockModel.class);
|
||||
model.name = pair.getFirst().toString();
|
||||
modelFiles.addAll(model.getDependencies());
|
||||
|
|
@ -306,6 +305,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
}
|
||||
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
|
||||
}
|
||||
UVController.useDummyUv.set(Boolean.FALSE);
|
||||
}
|
||||
modelFiles = null;
|
||||
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> {
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
public class BlockColorsMixin {
|
||||
private Lock mapLock = new ReentrantLock();
|
||||
@Inject(method = "register", at = @At("HEAD"))
|
||||
private void lockMapBeforeAccess(BlockColor pBlockColor, Block[] pBlocks, CallbackInfo ci) {
|
||||
private void lockMapBeforeAccess(CallbackInfo ci) {
|
||||
mapLock.lock();
|
||||
}
|
||||
@Inject(method = "register", at = @At("TAIL"))
|
||||
private void unlockMap(BlockColor pBlockColor, Block[] pBlocks, CallbackInfo ci) {
|
||||
private void unlockMap(CallbackInfo ci) {
|
||||
mapLock.unlock();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package org.embeddedt.modernfix.mixin.safety;
|
||||
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
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.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Mixin(value = ItemColors.class, priority = 700)
|
||||
public class ItemColorsMixin {
|
||||
private Lock mapLock = new ReentrantLock();
|
||||
@Inject(method = "register", at = @At("HEAD"))
|
||||
private void lockMapBeforeAccess(CallbackInfo ci) {
|
||||
mapLock.lock();
|
||||
}
|
||||
@Inject(method = "register", at = @At("TAIL"))
|
||||
private void unlockMap(CallbackInfo ci) {
|
||||
mapLock.unlock();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.spongepowered.asm.mixin.transformer.ClassInfo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClassInfoManager {
|
||||
private static Map<String, ClassInfo> classInfoCache = null;
|
||||
public static void clear() {
|
||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager"))
|
||||
return;
|
||||
if(classInfoCache == null) {
|
||||
try {
|
||||
Field field = ClassInfo.class.getDeclaredField("cache");
|
||||
field.setAccessible(true);
|
||||
classInfoCache = (Map<String, ClassInfo>)field.get(null);
|
||||
} catch(ReflectiveOperationException | RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
classInfoCache.entrySet().removeIf(entry -> !entry.getKey().equals("java/lang/Object") && (entry.getValue() == null || !entry.getValue().isMixin()));
|
||||
} catch(RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,25 +9,29 @@ import org.slf4j.Logger;
|
|||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class IntegratedWatchdog extends Thread {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final MinecraftServer server;
|
||||
private final WeakReference<MinecraftServer> server;
|
||||
|
||||
private static final long MAX_TICK_DELTA = 40*1000;
|
||||
|
||||
public IntegratedWatchdog(MinecraftServer server) {
|
||||
this.server = server;
|
||||
this.server = new WeakReference<>(server);
|
||||
this.setDaemon(true);
|
||||
this.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER));
|
||||
this.setName("ModernFix integrated server watchdog");
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(server.isRunning()) {
|
||||
long nextTick = this.server.getNextTickTime();
|
||||
while(true) {
|
||||
MinecraftServer server = this.server.get();
|
||||
if(server == null || !server.isRunning())
|
||||
return;
|
||||
long nextTick = server.getNextTickTime();
|
||||
long curTime = Util.getMillis();
|
||||
long delta = curTime - nextTick;
|
||||
if(delta > MAX_TICK_DELTA) {
|
||||
|
|
@ -53,6 +57,7 @@ public class IntegratedWatchdog extends Thread {
|
|||
nextTick = 0;
|
||||
curTime = 0;
|
||||
}
|
||||
server = null; /* allow GC */
|
||||
try {
|
||||
Thread.sleep(nextTick + MAX_TICK_DELTA - curTime);
|
||||
} catch(InterruptedException ignored) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
"perf.state_definition_construct.StateDefinitionMixin",
|
||||
"perf.compress_blockstate.BlockStateBaseMixin",
|
||||
"perf.compress_blockstate.BlockBehaviourMixin",
|
||||
"perf.dedup_blockstate_flattening_map.BlockStateDataMixin",
|
||||
"perf.dedup_blockstate_flattening_map.ChunkPalettedStorageFixMixin",
|
||||
"devenv.MinecraftServerMixin"
|
||||
],
|
||||
"client": [
|
||||
|
|
@ -42,6 +44,7 @@
|
|||
"feature.measure_time.MinecraftMixin",
|
||||
"feature.reduce_loading_screen_freezes.ModelBakeryMixin",
|
||||
"bugfix.concurrency.MinecraftMixin",
|
||||
"perf.dynamic_resources.BlockElementFaceDeserializerMixin",
|
||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||
"perf.dynamic_resources.ModelBakeryMixin",
|
||||
|
|
@ -55,6 +58,7 @@
|
|||
"perf.model_optimizations.PropertyMixin",
|
||||
"perf.thread_priorities.IntegratedServerMixin",
|
||||
"safety.BlockColorsMixin",
|
||||
"safety.ItemColorsMixin",
|
||||
"perf.flatten_model_predicates.AndConditionMixin",
|
||||
"perf.flatten_model_predicates.OrConditionMixin",
|
||||
"perf.flatten_model_predicates.PropertyValueConditionMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user