diff --git a/src/main/java/org/embeddedt/modernfix/ModernFix.java b/src/main/java/org/embeddedt/modernfix/ModernFix.java index 14dddb81..1a8942d0 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFix.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFix.java @@ -131,7 +131,7 @@ public class ModernFix { if(ModList.get().isLoaded(modId)) return true; } - return false; + return !FMLLoader.isProduction(); } @SubscribeEvent diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 5ae2bbd7..692a2c39 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -2,7 +2,10 @@ package org.embeddedt.modernfix.core.config; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator; +import net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.forgespi.locating.IModLocator; +import net.minecraftforge.forgespi.locating.IModProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -59,7 +62,6 @@ 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); @@ -68,6 +70,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.fast_forge_dummies", true); this.addMixinRule("perf.dynamic_structure_manager", true); this.addMixinRule("bugfix.chunk_deadlock", true); + this.addMixinRule("bugfix.remove_block_chunkloading", true); this.addMixinRule("bugfix.paper_chunk_patches", true); this.addMixinRule("perf.thread_priorities", true); this.addMixinRule("perf.scan_cache", true); @@ -80,6 +83,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.nbt_memory_usage", true); this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli")); this.addMixinRule("perf.datapack_reload_exceptions", true); + this.addMixinRule("perf.dynamic_dfu", true); this.addMixinRule("perf.faster_texture_stitching", true); /* off by default in 1.18 because it doesn't work as well */ this.addMixinRule("perf.faster_singleplayer_load", false); @@ -87,8 +91,10 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null || FMLLoader.getLoadingModList().getModFileById("roughlyenoughitems") != null); this.addMixinRule("safety", true); this.addMixinRule("launch.class_search_cache", true); - boolean isDevEnv = !FMLLoader.isProduction() && FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getProvider() instanceof ExplodedDirectoryLocator; + IModProvider mfLocator = FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getProvider(); + boolean isDevEnv = !FMLLoader.isProduction() && (mfLocator instanceof ExplodedDirectoryLocator || mfLocator instanceof MinecraftLocator); this.addMixinRule("devenv", isDevEnv); + this.addMixinRule("perf.remove_spawn_chunks", isDevEnv); /* Mod compat */ disableIfModPresent("mixin.perf.thread_priorities", "smoothboot"); diff --git a/src/main/java/org/embeddedt/modernfix/dfu/LazyDataFixer.java b/src/main/java/org/embeddedt/modernfix/dfu/LazyDataFixer.java new file mode 100644 index 00000000..dd9dfbdb --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dfu/LazyDataFixer.java @@ -0,0 +1,96 @@ +package org.embeddedt.modernfix.dfu; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.constant.EmptyPart; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.serialization.Dynamic; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.SharedConstants; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; + +public class LazyDataFixer implements DataFixer { + private static final Logger LOGGER = LogManager.getLogger("ModernFix"); + private DataFixer backingDataFixer; + private final Supplier dfuSupplier; + private static final Schema FAKE_SCHEMA = new EmptySchema(); + + public LazyDataFixer(Supplier dfuSupplier) { + LOGGER.info("Bypassed Mojang DFU"); + this.backingDataFixer = null; + this.dfuSupplier = dfuSupplier; + } + @Override + public Dynamic update(DSL.TypeReference type, Dynamic input, int version, int newVersion) { + if(version >= newVersion) + return input; + synchronized (this) { + if(backingDataFixer == null) { + LOGGER.info("Instantiating Mojang DFU"); + backingDataFixer = dfuSupplier.get(); + } + } + return backingDataFixer.update(type, input, version, newVersion); + } + + /** + * "getSchema is only there for checks that are not important" - fry, 2021 + */ + @Override + public Schema getSchema(int key) { + return FAKE_SCHEMA; + } + + /** + * Empty schema that also returns empty Type instances to prevent crashes. + */ + static class EmptySchema extends Schema { + public EmptySchema() { + super(DataFixUtils.makeKey(SharedConstants.getCurrentVersion().getWorldVersion()), null); + } + + private static final Type EMPTY_TYPE = new EmptyPart(); + private static final TypeTemplate FAKE_TEMPLATE = EMPTY_TYPE.template(); + + + @Override + protected Map> buildTypes() { + Object2ObjectOpenHashMap> map = new Object2ObjectOpenHashMap<>(); + map.defaultReturnValue(new EmptyPart()); + return map; + } + + @Override + public TypeTemplate resolveTemplate(String name) { + return FAKE_TEMPLATE; + } + + @Override + public Type getChoiceType(DSL.TypeReference type, String choiceName) { + return EMPTY_TYPE; + } + + @Override + public void registerTypes(Schema schema, Map> entityTypes, Map> blockEntityTypes) { + } + + @Override + public Map> registerEntities(Schema schema) { + return Collections.emptyMap(); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + return Collections.emptyMap(); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java deleted file mode 100644 index 176ac2c6..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/devenv/MinecraftServerMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.embeddedt.modernfix.mixin.devenv; - -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -/* Disable waiting for spawn chunk load */ -@Mixin(MinecraftServer.class) -public class MinecraftServerMixin { - @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I")) - private int noSpawnChunkWait(ServerChunkCache cache) { - return 441; - } - - @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;waitUntilNextTick()V")) - private void noWaitTick(MinecraftServer server) { - - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/BlockStateDataMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/BlockStateDataMixin.java deleted file mode 100644 index 63da3acc..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/BlockStateDataMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -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."); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/ChunkPalettedStorageFixMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/ChunkPalettedStorageFixMixin.java deleted file mode 100644 index 962a7fee..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/dedup_blockstate_flattening_map/ChunkPalettedStorageFixMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -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 = "", 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 = "", 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> cir) { - throw new UnsupportedOperationException("No Flattening for you."); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_dfu/DataFixersMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_dfu/DataFixersMixin.java new file mode 100644 index 00000000..f8e48cfa --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_dfu/DataFixersMixin.java @@ -0,0 +1,30 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_dfu; + +import com.mojang.datafixers.DataFixer; +import net.minecraft.util.datafix.DataFixers; +import org.embeddedt.modernfix.dfu.LazyDataFixer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(DataFixers.class) +public abstract class DataFixersMixin { + @Shadow protected static DataFixer createFixerUpper() { + throw new AssertionError(); + } + + private static LazyDataFixer lazyDataFixer; + + /** + * Avoid classloading the DFU logic until we actually need it. + */ + @Inject(method = "createFixerUpper", at = @At("HEAD"), cancellable = true) + private static void createLazyFixerUpper(CallbackInfoReturnable cir) { + if(lazyDataFixer == null) { + lazyDataFixer = new LazyDataFixer(DataFixersMixin::createFixerUpper); + cir.setReturnValue(lazyDataFixer); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java index 71becac6..b6ade380 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java @@ -10,6 +10,7 @@ import net.minecraft.world.item.Item; import net.minecraftforge.client.model.ForgeItemModelShaper; import net.minecraftforge.registries.ForgeRegistries; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +import org.embeddedt.modernfix.util.ItemMesherMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; @@ -38,71 +39,7 @@ public abstract class ItemModelShaperMixin extends ItemModelShaper { private void replaceLocationMap(CallbackInfo ci) { overrideLocations = new HashMap<>(); // need to replace this map because mods query locations through it - locations = new Map, ModelResourceLocation>() { - @Override - public int size() { - return ForgeRegistries.ITEMS.getValues().size(); - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean containsKey(Object key) { - return true; - } - - @Override - public boolean containsValue(Object value) { - return false; - } - - @Override - public ModelResourceLocation get(Object key) { - return getLocation(((Holder.Reference)key).get()); - } - - @Nullable - @Override - public ModelResourceLocation put(Holder.Reference key, ModelResourceLocation value) { - throw new UnsupportedOperationException(); - } - - @Override - public ModelResourceLocation remove(Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public void putAll(@NotNull Map, ? extends ModelResourceLocation> m) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public Set> keySet() { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public Collection values() { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public Set, ModelResourceLocation>> entrySet() { - throw new UnsupportedOperationException(); - } - }; + locations = new ItemMesherMap(this::getLocation); } private ModelResourceLocation getLocation(Item item) { diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/PathPackResourcesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/PathPackResourcesMixin.java index b38de21e..fedf881a 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/PathPackResourcesMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/PathPackResourcesMixin.java @@ -1,15 +1,11 @@ package org.embeddedt.modernfix.mixin.perf.modern_resourcepacks; -import com.google.common.base.Joiner; -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.resources.PackResourcesCacheEngine; import org.embeddedt.modernfix.util.PackTypeHelper; -import org.spongepowered.asm.mixin.Final; +import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -17,14 +13,9 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * The built-in resource caching provided by Forge is overengineered and doesn't work correctly @@ -36,107 +27,39 @@ public abstract class PathPackResourcesMixin { @Shadow protected abstract Path resolve(String... paths); - @Shadow @Final private String packName; - @Shadow @Final private Path source; + @Shadow @NotNull + protected abstract Set getNamespacesFromDisk(PackType type); - @Shadow @NotNull protected abstract Set getNamespacesFromDisk(PackType type); - - private EnumMap> namespacesByType; - private EnumMap>> rootListingByNamespaceAndType; - private boolean hasGeneratedListings; - private Set containedPaths; - - private FileSystem resourcePackFS; - - private static Joiner slashJoiner = Joiner.on('/'); + private PackResourcesCacheEngine cacheEngine; @Inject(method = "", at = @At("TAIL")) private void cacheResources(String packName, Path source, CallbackInfo ci) { - this.resourcePackFS = source.getFileSystem(); - this.namespacesByType = new EnumMap<>(PackType.class); - this.hasGeneratedListings = false; + this.cacheEngine = null; } private void generateResourceCache() { synchronized (this) { - if(hasGeneratedListings) + if(this.cacheEngine != null) return; - EnumMap>> rootListingByNamespaceAndType = new EnumMap<>(PackType.class); - HashSet containedPaths = new HashSet<>(); - for(PackType type : PackType.values()) { - Set namespaces = this.getNamespacesFromDisk(type); - HashMap> rootListingForNamespaces = new HashMap<>(); - for(String namespace : namespaces) { - try { - Path root = this.resolve(type.getDirectory(), namespace).toAbsolutePath(); - try (Stream stream = Files.walk(root)) { - ArrayList rootListingPaths = new ArrayList<>(); - stream - .map(path -> root.relativize(path.toAbsolutePath())) - .filter(this::isValidCachedResourcePath) - .forEach(path -> { - 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); - } - } catch(IOException e) { - rootListingForNamespaces.put(namespace, Collections.emptyList()); - } - } - if(PackTypeHelper.isVanillaPackType(type)) - rootListingByNamespaceAndType.put(type, rootListingForNamespaces); - } - this.rootListingByNamespaceAndType = rootListingByNamespaceAndType; - this.containedPaths = containedPaths; - this.hasGeneratedListings = true; + this.cacheEngine = new PackResourcesCacheEngine(this::getNamespacesFromDisk, (type, namespace) -> this.resolve(type.getDirectory(), namespace)); } } - 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; - } - } - return true; - } - @Inject(method = "getNamespaces", at = @At("HEAD"), cancellable = true) private void useCacheForNamespaces(PackType type, CallbackInfoReturnable> cir) { if(!PackTypeHelper.isVanillaPackType(type)) return; - Set cachedNamespaces; - synchronized (this.namespacesByType) { - cachedNamespaces = this.namespacesByType.get(type); - } - if(cachedNamespaces != null) { - cir.setReturnValue(cachedNamespaces); - } - } - - @Inject(method = "getNamespaces", at = @At("TAIL")) - private void storeCacheForNamespaces(PackType type, CallbackInfoReturnable> cir) { - if(!PackTypeHelper.isVanillaPackType(type)) - return; - synchronized (this.namespacesByType) { - this.namespacesByType.put(type, cir.getReturnValue()); + if(this.cacheEngine != null) { + Set namespaces = this.cacheEngine.getNamespaces(type); + if(namespaces != null) + cir.setReturnValue(namespaces); } } //@Inject(method = "hasResource(Ljava/lang/String;)Z", at = @At(value = "HEAD"), cancellable = true) private void useCacheForExistence(String path, CallbackInfoReturnable cir) { this.generateResourceCache(); - cir.setReturnValue(this.containedPaths.contains(new CachedResourcePath(path))); + cir.setReturnValue(this.cacheEngine.hasResource(path)); } /** @@ -149,17 +72,6 @@ public abstract class PathPackResourcesMixin { if(!PackTypeHelper.isVanillaPackType(type)) return; this.generateResourceCache(); - if(!pathIn.endsWith("/")) - pathIn = pathIn + "/"; - final String testPath = pathIn; - Collection cachedListing = this.rootListingByNamespaceAndType.get(type).getOrDefault(resourceNamespace, Collections.emptyList()).stream(). - 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.getFullPath())). - filter(filter::test). // Test the file name against the predicate - collect(Collectors.toList()); - cir.setReturnValue(cachedListing); + cir.setReturnValue(this.cacheEngine.getResources(type, resourceNamespace, pathIn, Integer.MAX_VALUE, filter)); } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java index 8de18d38..a9e36b28 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java @@ -3,6 +3,7 @@ package org.embeddedt.modernfix.mixin.perf.modern_resourcepacks; import com.google.common.base.Joiner; import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.server.packs.PackType; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.VanillaPackResources; @@ -41,7 +42,7 @@ public class VanillaPackResourcesMixin { private void cacheContainedPaths(PackMetadataSection arg, String[] p_i47912_1_, CallbackInfo ci) { if(containedPaths != null) return; - containedPaths = new HashSet<>(); + containedPaths = new ObjectOpenHashSet<>(); Joiner slashJoiner = Joiner.on('/'); for(PackType type : PackType.values()) { if(!PackTypeHelper.isVanillaPackType(type)) @@ -59,6 +60,7 @@ public class VanillaPackResourcesMixin { e.printStackTrace(); } } + ((ObjectOpenHashSet)containedPaths).trim(); } //@Redirect(method = "getResources(Ljava/util/Collection;Ljava/lang/String;Ljava/nio/file/Path;Ljava/lang/String;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;walk(Ljava/nio/file/Path;[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;")) diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/MinecraftServerMixin.java new file mode 100644 index 00000000..ab053461 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/MinecraftServerMixin.java @@ -0,0 +1,26 @@ +package org.embeddedt.modernfix.mixin.perf.remove_spawn_chunks; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.DistanceManager; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.TicketType; +import net.minecraft.util.Unit; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = MinecraftServer.class, priority = 1100) +public class MinecraftServerMixin { + @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V")) + private void addSpawnChunkTicket(ServerChunkCache cache, TicketType type, ChunkPos pos, int distance, Object o) { + // load first chunk + cache.getChunk(pos.x, pos.z, ChunkStatus.FULL, true); + } + + @Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"), require = 0) + private int getGenerated(ServerChunkCache cache) { + return 441; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerChunkCacheAccessor.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerChunkCacheAccessor.java new file mode 100644 index 00000000..76ed3c26 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerChunkCacheAccessor.java @@ -0,0 +1,12 @@ +package org.embeddedt.modernfix.mixin.perf.remove_spawn_chunks; + +import net.minecraft.server.level.DistanceManager; +import net.minecraft.server.level.ServerChunkCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ServerChunkCache.class) +public interface ServerChunkCacheAccessor { + @Accessor("distanceManager") + DistanceManager getDistanceManager(); +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerLevelMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerLevelMixin.java new file mode 100644 index 00000000..7e61e732 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/remove_spawn_chunks/ServerLevelMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.mixin.perf.remove_spawn_chunks; + +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; +import net.minecraft.world.level.ChunkPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ServerLevel.class) +public class ServerLevelMixin { + @Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;removeRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V")) + private void removeTicket(ServerChunkCache cache, TicketType type, ChunkPos pos, int distance, Object o) { + } + + @Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V")) + private void addTicket(ServerChunkCache cache, TicketType type, ChunkPos pos, int distance, Object o) { + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/CachedResourcePath.java b/src/main/java/org/embeddedt/modernfix/resources/CachedResourcePath.java similarity index 70% rename from src/main/java/org/embeddedt/modernfix/util/CachedResourcePath.java rename to src/main/java/org/embeddedt/modernfix/resources/CachedResourcePath.java index 889311ae..8f3d458f 100644 --- a/src/main/java/org/embeddedt/modernfix/util/CachedResourcePath.java +++ b/src/main/java/org/embeddedt/modernfix/resources/CachedResourcePath.java @@ -1,31 +1,23 @@ -package org.embeddedt.modernfix.util; +package org.embeddedt.modernfix.resources; -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 org.embeddedt.modernfix.util.FileUtil; -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 PATH_COMPONENT_INTERNER = Interners.newStrongInterner(); + public static final Interner PATH_COMPONENT_INTERNER = Interners.newStrongInterner(); private static final Splitter SLASH_SPLITTER = Splitter.on('/'); - private static final Joiner SLASH_JOINER = Joiner.on('/'); - private WeakReference fullPathCache = new WeakReference<>(null); private static final String[] NO_PREFIX = new String[0]; - public CachedResourcePath(Path path) { - this(NO_PREFIX, path, path.getNameCount(), true); + public CachedResourcePath(String[] prefix, Path path) { + this(prefix, path, path.getNameCount(), true); } public CachedResourcePath(String s) { @@ -67,11 +59,7 @@ public class CachedResourcePath { @Override public int hashCode() { - int result = hashCode; - if(result != 0) - return result; - hashCode = Arrays.hashCode(pathComponents); - return hashCode; + return Arrays.hashCode(pathComponents); } @Override @@ -90,12 +78,17 @@ public class CachedResourcePath { return pathComponents.length; } - public String getFullPath() { - String fPath = fullPathCache.get(); - if(fPath == null) { - fPath = SLASH_JOINER.join(pathComponents); - fullPathCache = new WeakReference<>(fPath); + public String getNameAt(int i) { + return pathComponents[i]; + } + + public String getFullPath(int startIndex) { + StringBuilder sb = new StringBuilder(); + for(int i = startIndex; i < pathComponents.length; i++) { + sb.append(pathComponents[i]); + if(i != (pathComponents.length - 1)) + sb.append('/'); } - return fPath; + return sb.toString(); } } diff --git a/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java b/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java new file mode 100644 index 00000000..df52e592 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java @@ -0,0 +1,116 @@ +package org.embeddedt.modernfix.resources; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; +import org.embeddedt.modernfix.util.PackTypeHelper; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * The core of the resource pack cache system. + * + * Using a dedicated set and also separate lists is important; testing without this showed a huge performance + * drop. + */ +public class PackResourcesCacheEngine { + private final Map> namespacesByType; + private final Set containedPaths; + private final EnumMap>> resourceListings; + + public PackResourcesCacheEngine(Function> namespacesRetriever, BiFunction basePathRetriever) { + this.namespacesByType = new EnumMap<>(PackType.class); + for(PackType type : PackType.values()) { + if(!PackTypeHelper.isVanillaPackType(type)) + continue; + this.namespacesByType.put(type, namespacesRetriever.apply(type)); + } + this.containedPaths = new ObjectOpenHashSet<>(); + this.resourceListings = new EnumMap<>(PackType.class); + for(PackType type : PackType.values()) { + Collection namespaces = PackTypeHelper.isVanillaPackType(type) ? this.namespacesByType.get(type) : namespacesRetriever.apply(type); + ImmutableMap.Builder> packTypedMap = ImmutableMap.builder(); + for(String namespace : namespaces) { + try { + ImmutableList.Builder namespacedList = ImmutableList.builder(); + Path root = basePathRetriever.apply(type, namespace).toAbsolutePath(); + String[] prefix = new String[] { type.getDirectory(), namespace }; + try (Stream stream = Files.walk(root)) { + stream + .map(path -> root.relativize(path.toAbsolutePath())) + .filter(PackResourcesCacheEngine::isValidCachedResourcePath) + .forEach(path -> { + CachedResourcePath cachedPath = new CachedResourcePath(prefix, path); + this.containedPaths.add(cachedPath); + if(!cachedPath.getFileName().endsWith(".mcmeta")) + namespacedList.add(cachedPath); + }); + } + packTypedMap.put(namespace, namespacedList.build()); + } catch(IOException ignored) { + } + } + this.resourceListings.put(type, packTypedMap.build()); + } + ((ObjectOpenHashSet)this.containedPaths).trim(); + } + + private static 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; + } + } + return true; + } + + public Set getNamespaces(PackType type) { + if(PackTypeHelper.isVanillaPackType(type)) + return this.namespacesByType.get(type); + else + return null; + } + + public boolean hasResource(String path) { + return this.containedPaths.contains(new CachedResourcePath(path)); + } + + public Collection getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate filter) { + if(!PackTypeHelper.isVanillaPackType(type)) + throw new IllegalArgumentException("Only vanilla PackTypes are supported"); + List paths = resourceListings.get(type).getOrDefault(resourceNamespace, Collections.emptyList()); + if(paths.isEmpty()) + return Collections.emptyList(); + String testPath = pathIn.endsWith("/") ? pathIn : (pathIn + "/"); + ArrayList resources = new ArrayList<>(); + for(CachedResourcePath cachePath : paths) { + if((cachePath.getNameCount() - 2) > maxDepth) + continue; + String fullPath = cachePath.getFullPath(2); + if(!fullPath.startsWith(testPath)) + continue; + ResourceLocation foundResource = new ResourceLocation(resourceNamespace, fullPath); + if(!filter.test(foundResource)) + continue; + resources.add(foundResource); + } + return resources; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/ItemMesherMap.java b/src/main/java/org/embeddedt/modernfix/util/ItemMesherMap.java new file mode 100644 index 00000000..94e1b2f2 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/ItemMesherMap.java @@ -0,0 +1,85 @@ +package org.embeddedt.modernfix.util; + +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Holder; +import net.minecraft.world.item.Item; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public class ItemMesherMap implements Map, ModelResourceLocation> { + private final Function getLocation; + + public ItemMesherMap(Function getLocation) { + this.getLocation = getLocation; + } + + @Override + public int size() { + return ForgeRegistries.ITEMS.getValues().size(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean containsKey(Object key) { + return true; + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public ModelResourceLocation get(Object key) { + return getLocation.apply(((Holder.Reference)key).get()); + } + + @Nullable + @Override + public ModelResourceLocation put(Holder.Reference key, ModelResourceLocation value) { + throw new UnsupportedOperationException(); + } + + @Override + public ModelResourceLocation remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(@NotNull Map, ? extends ModelResourceLocation> m) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set> keySet() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set, ModelResourceLocation>> entrySet() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/resources/assets/modernfix/lang/en_us.json b/src/main/resources/assets/modernfix/lang/en_us.json index ddd8c10b..108f5124 100644 --- a/src/main/resources/assets/modernfix/lang/en_us.json +++ b/src/main/resources/assets/modernfix/lang/en_us.json @@ -2,7 +2,7 @@ "key.modernfix": "ModernFix", "key.modernfix.config": "Open config screen", "modernfix.jei_load": "Loading JEI, this may take a while", - "modernfix.no_lazydfu": "ModernFix detected that DFU rules were compiled on startup. This slows down game launching. Installing LazyDFU to resolve this is highly recommended.", + "modernfix.no_lazydfu": "LazyDFU is not installed. If Minecraft needs to update game data from an older version, there may be noticeable lag.", "modernfix.config": "ModernFix mixin config", "modernfix.config.done_restart": "Done (restart required)", "modernfix.option.on": "on", diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index e87662d0..435e49e9 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -15,6 +15,7 @@ "bugfix.chunk_deadlock.ServerChunkCacheMixin", "perf.dedicated_reload_executor.MinecraftServerMixin", "perf.fast_forge_dummies.NamespacedHolderHelperMixin", + "perf.dynamic_dfu.DataFixersMixin", "perf.remove_biome_temperature_cache.BiomeMixin", "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", @@ -45,9 +46,9 @@ "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", + "perf.remove_spawn_chunks.ServerChunkCacheAccessor", + "perf.remove_spawn_chunks.MinecraftServerMixin", + "perf.remove_spawn_chunks.ServerLevelMixin", "devenv.GameDataMixin" ], "client": [