Merge remote-tracking branch 'origin/1.19.2' into 1.19.4
This commit is contained in:
commit
23ee5cf982
|
|
@ -108,9 +108,11 @@ tasks.withType(JavaCompile) {
|
|||
options.encoding = "UTF-8"
|
||||
|
||||
def targetVersion = 17
|
||||
/*
|
||||
if (JavaVersion.current().isJava9Compatible()) {
|
||||
options.release = targetVersion
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
java {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
@ -91,6 +104,7 @@ public class ModernFix {
|
|||
ModLoader.get().addWarning(new ModLoadingWarning(ModLoadingContext.get().getActiveContainer().getModInfo(), ModLoadingStage.COMMON_SETUP, "modernfix.no_lazydfu"));
|
||||
});
|
||||
}
|
||||
ObjectHolderClearer.clearThrowables();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
@ -99,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();
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import org.apache.logging.log4j.Logger;
|
|||
import org.embeddedt.modernfix.classloading.FastAccessTransformerList;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
||||
import org.embeddedt.modernfix.core.config.Option;
|
||||
import org.embeddedt.modernfix.dfu.DFUBlaster;
|
||||
import org.embeddedt.modernfix.load.ModWorkManagerQueue;
|
||||
import org.embeddedt.modernfix.util.DummyList;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
|
@ -42,6 +43,7 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
|||
|
||||
FastAccessTransformerList.attemptReplace();
|
||||
ModWorkManagerQueue.replace();
|
||||
DFUBlaster.blastMaps();
|
||||
|
||||
/* https://github.com/FabricMC/Mixin/pull/99 */
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -37,10 +37,13 @@ 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);
|
||||
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
|
||||
this.addMixinRule("perf.dynamic_structure_manager", true);
|
||||
this.addMixinRule("bugfix.chunk_deadlock", true);
|
||||
this.addMixinRule("perf.thread_priorities", true);
|
||||
this.addMixinRule("perf.scan_cache", true);
|
||||
|
|
@ -65,14 +68,17 @@ public class ModernFixEarlyConfig {
|
|||
disableIfModPresent("mixin.perf.async_jei", "modernui");
|
||||
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge");
|
||||
disableIfModPresent("mixin.bugfix.mc218112", "performant");
|
||||
disableIfModPresent("mixin.perf.faster_baking", "touhou_little_maid");
|
||||
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
|
||||
}
|
||||
|
||||
private void disableIfModPresent(String configName, String... ids) {
|
||||
for(String id : ids) {
|
||||
if(FMLLoader.getLoadingModList().getModFileById(id) != null) {
|
||||
this.options.get(configName).addModOverride(false, id);
|
||||
Option option = this.options.get(configName);
|
||||
if(option != null)
|
||||
option.addModOverride(false, id);
|
||||
else
|
||||
LOGGER.warn("Can't disable missing option {}", configName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
49
src/main/java/org/embeddedt/modernfix/dfu/DFUBlaster.java
Normal file
49
src/main/java/org/embeddedt/modernfix/dfu/DFUBlaster.java
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package org.embeddedt.modernfix.dfu;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.datafixers.RewriteResult;
|
||||
import com.mojang.datafixers.TypeRewriteRule;
|
||||
import com.mojang.datafixers.functions.PointFreeRule;
|
||||
import com.mojang.datafixers.types.Type;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class DFUBlaster {
|
||||
public static void blastMaps() {
|
||||
Cache<Pair<IntFunction<RewriteResult<?, ?>>, Integer>, RewriteResult<?, ?>> hmapApplyCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(200) /* should mean approximately 50MB used max */
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
Cache<Triple<Type<?>, TypeRewriteRule, PointFreeRule>, Optional<? extends RewriteResult<?, ?>>> rewriteCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
try {
|
||||
Class<?> FOLD_CLASS = Class.forName("com.mojang.datafixers.functions.Fold");
|
||||
Field hmapField = FOLD_CLASS.getDeclaredField("HMAP_APPLY_CACHE");
|
||||
hmapField.setAccessible(true);
|
||||
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
Unsafe unsafe = (Unsafe)theUnsafe.get(null);
|
||||
Object base = unsafe.staticFieldBase(hmapField);
|
||||
long offset = unsafe.staticFieldOffset(hmapField);
|
||||
unsafe.putObject(base, offset, hmapApplyCache.asMap());
|
||||
Field rewriteCacheField = Type.class.getDeclaredField("REWRITE_CACHE");
|
||||
rewriteCacheField.setAccessible(true);
|
||||
base = unsafe.staticFieldBase(rewriteCacheField);
|
||||
offset = unsafe.staticFieldOffset(rewriteCacheField);
|
||||
unsafe.putObject(base, offset, rewriteCache.asMap());
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Could not replace DFU map", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface ICachedMaterialsModel {
|
||||
public void clearMaterialsCache();
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface IDynamicModelBakery {
|
||||
BakedModel bakeDefault(ResourceLocation modelLocation);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
|
||||
public interface IExtendedModelBakery {
|
||||
ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location);
|
||||
BakedModel bakeDefault(ResourceLocation modelLocation);
|
||||
}
|
||||
|
|
@ -2,13 +2,12 @@ package org.embeddedt.modernfix.dynamicresources;
|
|||
|
||||
import com.mojang.math.Transformation;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.duck.IDynamicModelBakery;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
|
@ -55,7 +54,7 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
|
|||
@Override
|
||||
public BakedModel get(Object o) {
|
||||
BakedModel model = permanentOverrides.get(o);
|
||||
return model != null ? model : ((IDynamicModelBakery)bakery).bakeDefault((ResourceLocation)o);
|
||||
return model != null ? model : ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -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,108 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.cache_model_materials;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(BlockModel.class)
|
||||
public class BlockModelMixin {
|
||||
@Shadow @Final @Mutable public Map<String, Either<Material, String>> textureMap;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason detect changes to the texture map, and clear the material cache as needed
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void useTrackingTextureMap(CallbackInfo ci) {
|
||||
Map<String, Either<Material, String>> backingMap = this.textureMap;
|
||||
ICachedMaterialsModel cacheHolder = (ICachedMaterialsModel)this;
|
||||
this.textureMap = new Map<String, Either<Material, String>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return backingMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backingMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return backingMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return backingMap.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> get(Object o) {
|
||||
return backingMap.get(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Either<Material, String> put(String s, Either<Material, String> materialStringEither) {
|
||||
Either<Material, String> old = backingMap.put(s, materialStringEither);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> remove(Object o) {
|
||||
Either<Material, String> e = backingMap.remove(o);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends String, ? extends Either<Material, String>> map) {
|
||||
backingMap.putAll(map);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
backingMap.clear();
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Either<Material, String>> values() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<String, Either<Material, String>>> entrySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.entrySet();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,21 +7,18 @@ import com.google.common.cache.RemovalNotification;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.math.Transformation;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemModelGenerator;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraftforge.client.model.geometry.GeometryLoaderManager;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IDynamicModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
|
@ -39,7 +36,7 @@ import java.util.function.BiFunction;
|
|||
|
||||
/* high priority so that our injectors are added before other mods' */
|
||||
@Mixin(value = ModelBakery.class, priority = 600)
|
||||
public abstract class ModelBakeryMixin implements IDynamicModelBakery {
|
||||
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||
|
||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
|
|
@ -268,8 +265,13 @@ public abstract class ModelBakeryMixin implements IDynamicModelBakery {
|
|||
|
||||
@Override
|
||||
public BakedModel bakeDefault(ResourceLocation modelLocation) {
|
||||
ModelBakery self = (ModelBakery)(Object)this;
|
||||
ModelBakery self = (ModelBakery) (Object) this;
|
||||
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
|
||||
return theBaker.bake(modelLocation, BlockModelRotation.X0_Y0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
|
||||
return loadOnlyRelevantBlockState(stateDefinition, location);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
|
|
@ -9,13 +10,17 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
|
|||
import net.minecraft.client.resources.model.MultiPartBakedModel;
|
||||
import net.minecraft.client.resources.model.WeightedBakedModel;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.ChunkRenderTypeSet;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -28,18 +33,22 @@ import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
|||
import team.chisel.ctm.client.util.CTMPackReloadListener;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(CTMPackReloadListener.class)
|
||||
public abstract class CTMPackReloadListenerMixin {
|
||||
/* caches the original render checks */
|
||||
@Shadow @Final private static Map<Holder.Reference<Block>, Predicate<RenderType>> blockRenderChecks;
|
||||
|
||||
private static Map<Holder.Reference<Block>, Predicate<RenderType>> renderCheckOverrides = new ConcurrentHashMap<>();
|
||||
|
||||
private static ChunkRenderTypeSet DEFAULT_TYPE_SET = ChunkRenderTypeSet.of(RenderType.solid());
|
||||
|
||||
@Shadow protected abstract Predicate<RenderType> getLayerCheck(BlockState state, BakedModel model);
|
||||
|
||||
@Shadow protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block);
|
||||
|
||||
private Map<ModelResourceLocation, BlockState> locationToState = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onInit(CallbackInfo ci) {
|
||||
MinecraftForge.EVENT_BUS.addListener(EventPriority.LOW, this::onModelBake);
|
||||
|
|
@ -47,31 +56,48 @@ public abstract class CTMPackReloadListenerMixin {
|
|||
|
||||
@Overwrite(remap = false)
|
||||
private void refreshLayerHacks() {
|
||||
blockRenderChecks.forEach((b, p) -> ItemBlockRenderTypes.setRenderLayer((Block) b.get(), p));
|
||||
blockRenderChecks.clear();
|
||||
if(locationToState.isEmpty()) {
|
||||
renderCheckOverrides.clear();
|
||||
if(blockRenderChecks.isEmpty()) {
|
||||
for(Block block : ForgeRegistries.BLOCKS.getValues()) {
|
||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||
locationToState.put(BlockModelShaper.stateToModelLocation(state), state);
|
||||
}
|
||||
Holder.Reference<Block> holder = ForgeRegistries.BLOCKS.getDelegateOrThrow(block);
|
||||
ChunkRenderTypeSet original = this.getExistingRenderCheck(block);
|
||||
if(original == null)
|
||||
original = DEFAULT_TYPE_SET;
|
||||
blockRenderChecks.put(holder, original::contains);
|
||||
ItemBlockRenderTypes.setRenderLayer(block, type -> this.useOverrideIfPresent(holder, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useOverrideIfPresent(Holder.Reference<Block> delegate, RenderType type) {
|
||||
Predicate<RenderType> override = renderCheckOverrides.get(delegate);
|
||||
if(override == null)
|
||||
override = blockRenderChecks.get(delegate);
|
||||
return override.test(type);
|
||||
}
|
||||
|
||||
private void onModelBake(DynamicModelBakeEvent event) {
|
||||
if(!(event.getModel() instanceof AbstractCTMBakedModel || event.getModel() instanceof WeightedBakedModel || event.getModel() instanceof MultiPartBakedModel))
|
||||
return;
|
||||
BlockState state = locationToState.get(event.getLocation());
|
||||
if(state == null)
|
||||
/* we construct a new ResourceLocation because an MRL is coming in */
|
||||
Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(event.getLocation().getNamespace(), event.getLocation().getPath()));
|
||||
Holder.Reference<Block> delegate = block != null ? ForgeRegistries.BLOCKS.getDelegateOrThrow(block) : null;
|
||||
if(block == null || block == Blocks.AIR || renderCheckOverrides.containsKey(delegate))
|
||||
return;
|
||||
Block block = state.getBlock();
|
||||
Holder.Reference<Block> delegate = ForgeRegistries.BLOCKS.getDelegateOrThrow(block);
|
||||
if(blockRenderChecks.containsKey(delegate))
|
||||
/* find all states that match this MRL */
|
||||
ImmutableList<BlockState> allStates;
|
||||
try {
|
||||
allStates = ((IExtendedModelBakery)(Object)event.getModelLoader()).getBlockStatesForMRL(block.getStateDefinition(), (ModelResourceLocation)event.getLocation());
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Couldn't get state for MRL " + event.getLocation(), e);
|
||||
return;
|
||||
Predicate<RenderType> newPredicate = this.getLayerCheck(state, event.getModel());
|
||||
if(newPredicate != null) {
|
||||
blockRenderChecks.put(delegate, this.getExistingRenderCheck(block)::contains);
|
||||
ItemBlockRenderTypes.setRenderLayer(block, newPredicate);
|
||||
}
|
||||
for(BlockState state : allStates) {
|
||||
Predicate<RenderType> newPredicate = this.getLayerCheck(state, event.getModel());
|
||||
if(newPredicate != null) {
|
||||
renderCheckOverrides.put(delegate, newPredicate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_structure_manager;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.core.HolderGetter;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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.Optional;
|
||||
|
||||
@Mixin(StructureTemplateManager.class)
|
||||
public class StructureManagerMixin {
|
||||
@Shadow @Final @Mutable
|
||||
private Map<ResourceLocation, Optional<StructureTemplate>> structureRepository;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void makeStructuresSafe(ResourceManager arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer, HolderGetter<Block> arg3, CallbackInfo ci) {
|
||||
/* Structures needing to be reloaded is not a huge issue since we optimize loading them already */
|
||||
Cache<ResourceLocation, Optional<StructureTemplate>> structureCache = CacheBuilder.newBuilder()
|
||||
.softValues()
|
||||
.build();
|
||||
this.structureRepository = structureCache.asMap();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,10 @@ import com.mojang.datafixers.util.Pair;
|
|||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.resource.PathPackResources;
|
||||
import org.embeddedt.modernfix.util.CachedResourcePath;
|
||||
import org.embeddedt.modernfix.util.FileUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.embeddedt.modernfix.util.PackTypeHelper;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
|
@ -40,9 +42,9 @@ public abstract class PathPackResourcesMixin {
|
|||
@Shadow @NotNull protected abstract Set<String> getNamespacesFromDisk(PackType type);
|
||||
|
||||
private EnumMap<PackType, Set<String>> namespacesByType;
|
||||
private EnumMap<PackType, HashMap<String, List<Pair<Path, String>>>> rootListingByNamespaceAndType;
|
||||
private EnumMap<PackType, HashMap<String, List<CachedResourcePath>>> rootListingByNamespaceAndType;
|
||||
private boolean hasGeneratedListings;
|
||||
private Set<String> containedPaths;
|
||||
private Set<CachedResourcePath> containedPaths;
|
||||
|
||||
private FileSystem resourcePackFS;
|
||||
|
||||
|
|
@ -59,24 +61,25 @@ public abstract class PathPackResourcesMixin {
|
|||
synchronized (this) {
|
||||
if(hasGeneratedListings)
|
||||
return;
|
||||
EnumMap<PackType, HashMap<String, List<Pair<Path, String>>>> rootListingByNamespaceAndType = new EnumMap<>(PackType.class);
|
||||
HashSet<String> containedPaths = new HashSet<>();
|
||||
EnumMap<PackType, HashMap<String, List<CachedResourcePath>>> rootListingByNamespaceAndType = new EnumMap<>(PackType.class);
|
||||
HashSet<CachedResourcePath> containedPaths = new HashSet<>();
|
||||
for(PackType type : PackType.values()) {
|
||||
Set<String> namespaces = this.getNamespacesFromDisk(type);
|
||||
HashMap<String, List<Pair<Path, String>>> rootListingForNamespaces = new HashMap<>();
|
||||
HashMap<String, List<CachedResourcePath>> rootListingForNamespaces = new HashMap<>();
|
||||
for(String namespace : namespaces) {
|
||||
try {
|
||||
Path root = this.resolve(type.getDirectory(), namespace).toAbsolutePath();
|
||||
try (Stream<Path> stream = Files.walk(root)) {
|
||||
ArrayList<Pair<Path, String>> rootListingPaths = new ArrayList<>();
|
||||
ArrayList<CachedResourcePath> rootListingPaths = new ArrayList<>();
|
||||
stream
|
||||
.map(path -> root.relativize(path.toAbsolutePath()))
|
||||
.filter(this::isValidCachedResourcePath)
|
||||
.forEach(path -> {
|
||||
if(!path.toString().endsWith(".mcmeta"))
|
||||
rootListingPaths.add(Pair.of(path, slashJoiner.join(path)));
|
||||
String mergedPath = slashJoiner.join(type.getDirectory(), namespace, path);
|
||||
containedPaths.add(mergedPath);
|
||||
CachedResourcePath listing = new CachedResourcePath(path);
|
||||
if(!listing.getFileName().endsWith(".mcmeta")) {
|
||||
rootListingPaths.add(listing);
|
||||
}
|
||||
containedPaths.add(new CachedResourcePath(new String[] { type.getDirectory(), namespace }, listing));
|
||||
});
|
||||
rootListingPaths.trimToSize();
|
||||
rootListingForNamespaces.put(namespace, rootListingPaths);
|
||||
|
|
@ -85,7 +88,8 @@ public abstract class PathPackResourcesMixin {
|
|||
rootListingForNamespaces.put(namespace, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
rootListingByNamespaceAndType.put(type, rootListingForNamespaces);
|
||||
if(PackTypeHelper.isVanillaPackType(type))
|
||||
rootListingByNamespaceAndType.put(type, rootListingForNamespaces);
|
||||
}
|
||||
this.rootListingByNamespaceAndType = rootListingByNamespaceAndType;
|
||||
this.containedPaths = containedPaths;
|
||||
|
|
@ -94,7 +98,11 @@ public abstract class PathPackResourcesMixin {
|
|||
}
|
||||
|
||||
private boolean isValidCachedResourcePath(Path path) {
|
||||
if(path.getFileName() == null || path.getNameCount() == 0)
|
||||
return false;
|
||||
String str = path.toString();
|
||||
if(str.length() == 0)
|
||||
return false;
|
||||
for(int i = 0; i < str.length(); i++) {
|
||||
if(!ResourceLocation.validPathChar(str.charAt(i))) {
|
||||
return false;
|
||||
|
|
@ -105,6 +113,8 @@ public abstract class PathPackResourcesMixin {
|
|||
|
||||
@Inject(method = "getNamespaces", at = @At("HEAD"), cancellable = true)
|
||||
private void useCacheForNamespaces(PackType type, CallbackInfoReturnable<Set<String>> cir) {
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
return;
|
||||
Set<String> cachedNamespaces;
|
||||
synchronized (this.namespacesByType) {
|
||||
cachedNamespaces = this.namespacesByType.get(type);
|
||||
|
|
@ -116,6 +126,8 @@ public abstract class PathPackResourcesMixin {
|
|||
|
||||
@Inject(method = "getNamespaces", at = @At("TAIL"))
|
||||
private void storeCacheForNamespaces(PackType type, CallbackInfoReturnable<Set<String>> cir) {
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
return;
|
||||
synchronized (this.namespacesByType) {
|
||||
this.namespacesByType.put(type, cir.getReturnValue());
|
||||
}
|
||||
|
|
@ -124,7 +136,7 @@ public abstract class PathPackResourcesMixin {
|
|||
//@Inject(method = "hasResource(Ljava/lang/String;)Z", at = @At(value = "HEAD"), cancellable = true)
|
||||
private void useCacheForExistence(String path, CallbackInfoReturnable<Boolean> cir) {
|
||||
this.generateResourceCache();
|
||||
cir.setReturnValue(this.containedPaths.contains(FileUtil.normalize(path)));
|
||||
cir.setReturnValue(this.containedPaths.contains(new CachedResourcePath(path)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -134,16 +146,18 @@ public abstract class PathPackResourcesMixin {
|
|||
@Inject(method = "getResources", at = @At("HEAD"), cancellable = true, remap = false)
|
||||
public void getResources(PackType type, String resourceNamespace, String pathIn, Predicate<ResourceLocation> filter, CallbackInfoReturnable<Collection<ResourceLocation>> cir)
|
||||
{
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
return;
|
||||
this.generateResourceCache();
|
||||
if(!pathIn.endsWith("/"))
|
||||
pathIn = pathIn + "/";
|
||||
final String testPath = pathIn;
|
||||
Collection<ResourceLocation> cachedListing = this.rootListingByNamespaceAndType.get(type).getOrDefault(resourceNamespace, Collections.emptyList()).stream().
|
||||
filter(path -> path.getSecond().startsWith(testPath)). // Make sure the target path is inside this one
|
||||
filter(path -> path.getFullPath().startsWith(testPath)). // Make sure the target path is inside this one
|
||||
// Finally we need to form the RL, so use the first name as the domain, and the rest as the path
|
||||
// It is VERY IMPORTANT that we do not rely on Path.toString as this is inconsistent between operating systems
|
||||
// Join the path names ourselves to force forward slashes
|
||||
map(path -> new ResourceLocation(resourceNamespace, path.getSecond())).
|
||||
map(path -> new ResourceLocation(resourceNamespace, path.getFullPath())).
|
||||
filter(filter::test). // Test the file name against the predicate
|
||||
collect(Collectors.toList());
|
||||
cir.setReturnValue(cachedListing);
|
||||
|
|
|
|||
|
|
@ -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,47 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.registries.ObjectHolderRegistry;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ObjectHolderClearer {
|
||||
/**
|
||||
* Many of the built-in Forge holders needlessly hold on to an exception.
|
||||
*/
|
||||
public static void clearThrowables() {
|
||||
Set<Consumer<Predicate<ResourceLocation>>> holders = ObfuscationReflectionHelper.getPrivateValue(ObjectHolderRegistry.class, null, "objectHolders");
|
||||
if(holders != null) {
|
||||
int numCleared = 0;
|
||||
HashMap<Class<?>, Field> throwableField = new HashMap<>();
|
||||
Throwable singletonThrowable = new Throwable("[This stacktrace was cleared to save memory]");
|
||||
try {
|
||||
for(Consumer<Predicate<ResourceLocation>> holder : holders) {
|
||||
Field target = throwableField.computeIfAbsent(holder.getClass(), clz -> {
|
||||
Field[] clzFields = clz.getDeclaredFields();
|
||||
for(Field f : clzFields) {
|
||||
if(Throwable.class.isAssignableFrom(f.getType())) {
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if(target != null) {
|
||||
target.set(holder, singletonThrowable);
|
||||
numCleared++;
|
||||
}
|
||||
}
|
||||
} catch(RuntimeException | ReflectiveOperationException | NoClassDefFoundError ignored) {
|
||||
}
|
||||
ModernFix.LOGGER.debug("Cleared " + numCleared + " object holder stacktrace references");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.google.common.collect.Streams;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CachedResourcePath {
|
||||
private final String[] pathComponents;
|
||||
private int hashCode = 0;
|
||||
|
||||
private static final Interner<String> PATH_COMPONENT_INTERNER = Interners.newStrongInterner();
|
||||
private static final Splitter SLASH_SPLITTER = Splitter.on('/');
|
||||
private static final Joiner SLASH_JOINER = Joiner.on('/');
|
||||
private WeakReference<String> fullPathCache = new WeakReference<>(null);
|
||||
private static final String[] NO_PREFIX = new String[0];
|
||||
|
||||
public CachedResourcePath(Path path) {
|
||||
this(NO_PREFIX, path, path.getNameCount());
|
||||
}
|
||||
|
||||
public CachedResourcePath(String s) {
|
||||
// normalize so we can guarantee there are no empty sections
|
||||
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)));
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection) {
|
||||
this(prefixElements, collection, collection.size());
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count) {
|
||||
String[] components = new String[prefixElements.length + count];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
||||
i++;
|
||||
}
|
||||
for(Object component : path) {
|
||||
String s = component.toString();
|
||||
if(s.length() == 0)
|
||||
continue;
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(s);
|
||||
i++;
|
||||
}
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
public CachedResourcePath(String[] prefixElements, CachedResourcePath other) {
|
||||
String[] components = new String[prefixElements.length + other.pathComponents.length];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
||||
i++;
|
||||
}
|
||||
System.arraycopy(other.pathComponents, 0, components, i, other.pathComponents.length);
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = hashCode;
|
||||
if(result != 0)
|
||||
return result;
|
||||
hashCode = Arrays.hashCode(pathComponents);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CachedResourcePath that = (CachedResourcePath) o;
|
||||
return Arrays.equals(pathComponents, that.pathComponents);
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return pathComponents[pathComponents.length - 1];
|
||||
}
|
||||
|
||||
public int getNameCount() {
|
||||
return pathComponents.length;
|
||||
}
|
||||
|
||||
public String getFullPath() {
|
||||
String fPath = fullPathCache.get();
|
||||
if(fPath == null) {
|
||||
fPath = SLASH_JOINER.join(pathComponents);
|
||||
fullPathCache = new WeakReference<>(fPath);
|
||||
}
|
||||
return fPath;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
public class PackTypeHelper {
|
||||
public static boolean isVanillaPackType(PackType type) {
|
||||
return type == PackType.CLIENT_RESOURCES || type == PackType.SERVER_DATA;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"refmap": "modernfix.refmap.json",
|
||||
"mixins": [
|
||||
"bugfix.edge_chunk_not_saved.ChunkManagerMixin",
|
||||
"perf.dynamic_structure_manager.StructureManagerMixin",
|
||||
"bugfix.chunk_deadlock.ServerChunkCacheMixin",
|
||||
"perf.remove_biome_temperature_cache.BiomeMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
|
||||
|
|
@ -31,6 +32,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": [
|
||||
|
|
@ -38,6 +41,7 @@
|
|||
"core.SynchedEntityDataMixin",
|
||||
"feature.measure_time.MinecraftMixin",
|
||||
"bugfix.concurrency.MinecraftMixin",
|
||||
"perf.dynamic_resources.BlockElementFaceDeserializerMixin",
|
||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||
"perf.dynamic_resources.ModelBakerImplMixin",
|
||||
|
|
@ -53,6 +57,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