diff --git a/build.gradle b/build.gradle index 8e2c395f..dbd4a66d 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,9 @@ dependencies { // compile against the JEI API but do not include it at runtime modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}") + modRuntimeOnly("curse.maven:jei-238222:4352925") + + modImplementation("curse.maven:jeresources-240630:3831559") } tasks.withType(JavaCompile) { diff --git a/src/main/java/org/embeddedt/modernfix/ModernFix.java b/src/main/java/org/embeddedt/modernfix/ModernFix.java index 6e0d7274..2af32f35 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFix.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFix.java @@ -4,18 +4,20 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.DistExecutor; -import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.ModLoadingContext; +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.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.loading.FMLLoader; 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.core.config.ModernFixConfig; +import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler; +import org.embeddedt.modernfix.packet.PacketHandler; + import java.lang.management.ManagementFactory; import java.util.concurrent.CountDownLatch; @@ -62,9 +64,30 @@ public class ModernFix { INSTANCE = this; // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.register(this); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup); 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(); + } + + private static boolean dfuModPresent() { + for(String modId : new String[] { "lazydfu", "datafixerslayer" }) { + if(ModList.get().isLoaded(modId)) + return true; + } + return false; + } + + @SubscribeEvent + public void commonSetup(FMLCommonSetupEvent event) { + if(!dfuModPresent()) { + event.enqueueWork(() -> { + ModLoader.get().addWarning(new ModLoadingWarning(ModLoadingContext.get().getActiveContainer().getModInfo(), ModLoadingStage.COMMON_SETUP, "modernfix.no_lazydfu")); + }); + } } @SubscribeEvent diff --git a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index a3175de9..ef61b7ae 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -1,9 +1,15 @@ package org.embeddedt.modernfix; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraftforge.client.event.ScreenOpenEvent; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.entity.Entity; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; @@ -11,12 +17,17 @@ import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; +import net.minecraftforge.network.NetworkEvent; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.load.LoadEvents; +import org.embeddedt.modernfix.packet.EntityIDSyncPacket; import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen; import java.lang.management.ManagementFactory; -import java.util.Optional; +import java.lang.reflect.Field; +import java.sql.Ref; +import java.util.*; +import java.util.function.Supplier; public class ModernFixClient { public static long worldLoadStartTime; @@ -71,4 +82,78 @@ public class ModernFixClient { event.getLeft().add(brandingString); } } + + /** + * Check if the IDs match and remap them if not. + * @return true if ID remap was needed + */ + private static boolean compareAndSwitchIds(Class eClass, String fieldName, EntityDataAccessor accessor, int newId) { + if(accessor.id != newId) { + ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.", + eClass, + fieldName, + accessor.id, + newId); + accessor.id = newId; + return true; + } else { + ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId); + return false; + } + } + + /** + * Horrendous hack to allow tracking every synced entity data manager. + * + * This is to ensure we can perform ID fixup on already constructed managers. + */ + public static Set allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>()); + + /** + * Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server. + * + * The technique is far from ideal, but it should detect reliably and also not break already constructed entities. + */ + public static void handleEntityIDSync(EntityIDSyncPacket packet, Supplier context) { + Map, List>> info = packet.getFieldInfo(); + context.get().enqueueWork(() -> { + boolean fixNeeded = false; + for(Map.Entry, List>> entry : info.entrySet()) { + Class eClass = entry.getKey(); + for(Pair field : entry.getValue()) { + String fieldName = field.getFirst(); + int newId = field.getSecond(); + try { + Field f = eClass.getDeclaredField(fieldName); + f.setAccessible(true); + EntityDataAccessor accessor = (EntityDataAccessor)f.get(null); + if(compareAndSwitchIds(eClass, fieldName, accessor, newId)) + fixNeeded = true; + } catch(NoSuchFieldException e) { + ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName); + } catch(ReflectiveOperationException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + } + /* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */ + List dataEntries; + synchronized (allEntityDatas) { + if(fixNeeded) { + dataEntries = new ArrayList<>(allEntityDatas); + for(SynchedEntityData manager : dataEntries) { + Int2ObjectMap> fixedMap = new Int2ObjectOpenHashMap<>(); + List> items = new ArrayList<>(manager.itemsById.values()); + for(SynchedEntityData.DataItem item : items) { + fixedMap.put(item.getAccessor().id, item); + } + manager.itemsById = fixedMap; + } + } + allEntityDatas.clear(); + } + }); + + context.get().setPacketHandled(true); + } } diff --git a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java index e9bd7c9f..4c439b5d 100644 --- a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java +++ b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java @@ -53,7 +53,7 @@ public class BlockStateCacheHandler { currentRebuildThread = null; } } else { - ModernFix.LOGGER.warn("Deferred blockstate cache rebuild"); + ModernFix.LOGGER.debug("Deferred blockstate cache rebuild"); } } 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 43ea82f2..1e525ee4 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -23,6 +23,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("feature.measure_time", true); this.addMixinRule("feature.reduce_loading_screen_freezes", false); this.addMixinRule("perf.fast_registry_validation", true); + this.addMixinRule("perf.use_integrated_resources", true); this.addMixinRule("perf.remove_biome_temperature_cache", true); this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true); this.addMixinRule("perf.parallelize_model_loading", true); @@ -31,6 +32,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.cache_upgraded_structures", true); this.addMixinRule("bugfix.concurrency", true); this.addMixinRule("bugfix.edge_chunk_not_saved", true); + this.addMixinRule("perf.async_jei", true); this.addMixinRule("perf.thread_priorities", true); this.addMixinRule("perf.sync_executor_sleep", true); this.addMixinRule("perf.scan_cache", true); diff --git a/src/main/java/org/embeddedt/modernfix/duck/reuse_datapacks/ICachingResourceClient.java b/src/main/java/org/embeddedt/modernfix/duck/reuse_datapacks/ICachingResourceClient.java new file mode 100644 index 00000000..030967d9 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/reuse_datapacks/ICachingResourceClient.java @@ -0,0 +1,10 @@ +package org.embeddedt.modernfix.duck.reuse_datapacks; + +import net.minecraft.server.ReloadableServerResources; + +import java.util.Collection; + +public interface ICachingResourceClient { + void setCachedResources(ReloadableServerResources r); + void setCachedDataPackConfig(Collection c); +} diff --git a/src/main/java/org/embeddedt/modernfix/entity/EntityDataIDSyncHandler.java b/src/main/java/org/embeddedt/modernfix/entity/EntityDataIDSyncHandler.java new file mode 100644 index 00000000..7be0a5ff --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/entity/EntityDataIDSyncHandler.java @@ -0,0 +1,69 @@ +package org.embeddedt.modernfix.entity; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.entity.Entity; +import net.minecraftforge.event.OnDatapackSyncEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; +import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.server.ServerLifecycleHooks; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.packet.EntityIDSyncPacket; +import org.embeddedt.modernfix.packet.PacketHandler; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EntityDataIDSyncHandler { + private static Map, List>> fieldsToSyncMap; + @SubscribeEvent(priority = EventPriority.HIGHEST) + @SuppressWarnings("unchecked") + public static void onDatapackSyncEvent(OnDatapackSyncEvent event) { + if(event.getPlayer() != null) { + if(!ServerLifecycleHooks.getCurrentServer().isDedicatedServer() && event.getPlayerList().getPlayerCount() == 0) { + ModernFix.LOGGER.debug("Not syncing IDs on integrated server"); + return; + } + /* Compute the current set of serializer IDs in use and send them */ + try { + if(fieldsToSyncMap == null) { + fieldsToSyncMap = new HashMap<>(); + Field entityPoolField = ObfuscationReflectionHelper.findField(SynchedEntityData.class, "f_135343_"); + Map, Integer> entityPoolMap = (Map, Integer>)entityPoolField.get(null); + List fieldsToSync = new ArrayList<>(); + for(Class eClass : entityPoolMap.keySet()) { + fieldsToSync.clear(); + Field[] classFields = eClass.getDeclaredFields(); + for(Field field : classFields) { + if(!Modifier.isStatic(field.getModifiers())) + continue; + field.setAccessible(true); + Object o = field.get(null); + if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) { + fieldsToSync.add(field); + } + } + for(Field field : fieldsToSync) { + int id = ((EntityDataAccessor)field.get(null)).id; + fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id)); + } + } + } + EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap); + ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes"); + PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(event::getPlayer), packet); + } catch(ObfuscationReflectionHelper.UnableToFindFieldException | ReflectiveOperationException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/core/SynchedEntityDataMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/core/SynchedEntityDataMixin.java new file mode 100644 index 00000000..67133e3f --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/core/SynchedEntityDataMixin.java @@ -0,0 +1,24 @@ +package org.embeddedt.modernfix.mixin.core; + +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.entity.Entity; +import org.embeddedt.modernfix.ModernFixClient; +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; + +@Mixin(SynchedEntityData.class) +public class SynchedEntityDataMixin { + /** + * Store this in our set of all entity data objects. + * + * Not an ideal solution, but it should guarantee compatibility with mods. + */ + @Inject(method = "", at = @At("RETURN")) + private void storeInSet(Entity arg, CallbackInfo ci) { + synchronized (ModernFixClient.allEntityDatas) { + ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this); + } + } +} 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 c0ca392e..9cd8f89f 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 @@ -9,6 +9,7 @@ import net.minecraft.server.packs.VanillaPackResources; import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import org.apache.commons.lang3.tuple.Pair; import org.embeddedt.modernfix.FileWalker; +import org.embeddedt.modernfix.util.FileUtil; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @@ -71,7 +72,7 @@ public class VanillaPackResourcesMixin { @Inject(method = "hasResource", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResource(Ljava/lang/String;)Ljava/net/URL;"), cancellable = true) private void useCacheForExistence(PackType type, ResourceLocation location, CallbackInfoReturnable cir) { - cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + location.getPath())); + cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + FileUtil.normalize(location.getPath()))); } /** diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/thread_priorities/IntegratedServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/thread_priorities/IntegratedServerMixin.java index 4f940e86..6f51f0b8 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/thread_priorities/IntegratedServerMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/thread_priorities/IntegratedServerMixin.java @@ -23,7 +23,6 @@ public class IntegratedServerMixin { @Inject(method = "", at = @At("RETURN")) private void adjustServerPriority(Thread thread, Minecraft arg, LevelStorageSource.LevelStorageAccess arg2, PackRepository arg3, WorldStem arg4, MinecraftSessionService minecraftSessionService, GameProfileRepository gameProfileRepository, GameProfileCache arg5, ChunkProgressListenerFactory arg6, CallbackInfo ci) { int pri = ModernFixConfig.INTEGRATED_SERVER_PRIORITY.get(); - ModernFix.LOGGER.info("Changing server thread priority to " + pri); thread.setPriority(pri); } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java new file mode 100644 index 00000000..8ec63f73 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/use_integrated_resources/LootTableHelperMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.mixin.perf.use_integrated_resources; + +import jeresources.util.LootTableHelper; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.Level; +import net.minecraftforge.server.ServerLifecycleHooks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(LootTableHelper.class) +public class LootTableHelperMixin { + @Redirect(method = "getLootTables", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getServer()Lnet/minecraft/server/MinecraftServer;")) + private static MinecraftServer useIntegrated(Level level) { + MinecraftServer server = level.getServer(); + if(server != null) + return server; + return ServerLifecycleHooks.getCurrentServer(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/packet/EntityIDSyncPacket.java b/src/main/java/org/embeddedt/modernfix/packet/EntityIDSyncPacket.java new file mode 100644 index 00000000..6fe2c6b1 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/packet/EntityIDSyncPacket.java @@ -0,0 +1,78 @@ +package org.embeddedt.modernfix.packet; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import org.embeddedt.modernfix.ModernFix; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.*; + +public class EntityIDSyncPacket { + private Map, List>> map; + + public EntityIDSyncPacket(Map, List>> map) { + this.map = map; + } + + public Map, List>> getFieldInfo() { + return this.map; + } + + public EntityIDSyncPacket() { + this.map = new HashMap<>(); + } + + public void serialize(FriendlyByteBuf buf) { + buf.writeVarInt(map.keySet().size()); + for(Map.Entry, List>> entry : map.entrySet()) { + buf.writeUtf(entry.getKey().getName()); + buf.writeVarInt(entry.getValue().size()); + for(Pair field : entry.getValue()) { + buf.writeUtf(field.getFirst()); + buf.writeVarInt(field.getSecond()); + } + } + } + + @SuppressWarnings("unchecked") + public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) { + EntityIDSyncPacket self = new EntityIDSyncPacket(); + int numEntityClasses = buf.readVarInt(); + for(int i = 0; i < numEntityClasses; i++) { + String clzName = buf.readUtf(); + try { + Class clz; + try { + clz = Class.forName(clzName); + } catch(ClassNotFoundException e) { + ModernFix.LOGGER.warn("Entity class not found: {}", clzName); + break; + } + if(!Entity.class.isAssignableFrom(clz)) { + ModernFix.LOGGER.error("Not an entity: " + clzName); + break; + } + int numFields = buf.readVarInt(); + for(int j = 0; j < numFields; j++) { + String fieldName = buf.readUtf(); + int id = buf.readVarInt(); + Field f = clz.getDeclaredField(fieldName); + if(!Modifier.isStatic(f.getModifiers())) + continue; + f.setAccessible(true); + if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) { + ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName); + continue; + } + self.map.computeIfAbsent((Class)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id)); + } + } catch(ReflectiveOperationException e) { + ModernFix.LOGGER.error("Error deserializing packet", e); + } + } + return self; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/packet/PacketHandler.java b/src/main/java/org/embeddedt/modernfix/packet/PacketHandler.java new file mode 100644 index 00000000..e489fea0 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/packet/PacketHandler.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.packet; + +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.simple.SimpleChannel; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.ModernFixClient; + +public class PacketHandler { + private static final String PROTOCOL_VERSION = "1"; + public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel( + new ResourceLocation(ModernFix.MODID, "main"), + () -> PROTOCOL_VERSION, + NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION), + NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION) + ); + + public static void register() { + int id = 1; + INSTANCE.registerMessage(id++, EntityIDSyncPacket.class, EntityIDSyncPacket::serialize, EntityIDSyncPacket::deserialize, ModernFixClient::handleEntityIDSync); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java index 7e8dda59..51045bda 100644 --- a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java +++ b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java @@ -1,10 +1,22 @@ package org.embeddedt.modernfix.util; import java.io.File; +import java.util.regex.Pattern; public class FileUtil { public static File childFile(File file) { file.getParentFile().mkdirs(); return file; } + + private static final Pattern SLASH_PATTERN = Pattern.compile("(?:\\\\+|\\/+)"); + + /** + * Normalize a path by removing double slashes, etc. + * @param path input path + * @return a normalized version of the path + */ + public static String normalize(String path) { + return SLASH_PATTERN.matcher(path).replaceAll("/"); + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 84ad721f..a366f6f0 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -8,4 +8,6 @@ public net.minecraft.client.renderer.model.ModelBakery field_217849_F # unbakedC public net.minecraft.client.renderer.texture.Stitcher$Holder public net.minecraft.util.thread.BlockableEventLoop m_18699_()V # runAllTasks public net.minecraft.server.MinecraftServer f_129726_ # nextTickTime -public net.minecraft.client.Minecraft f_90999_ # progressListener \ No newline at end of file +public net.minecraft.client.Minecraft f_90999_ # progressListener +public-f net.minecraft.network.syncher.EntityDataAccessor f_135010_ # id +public-f net.minecraft.network.syncher.SynchedEntityData f_135345_ # itemsById diff --git a/src/main/resources/assets/modernfix/lang/en_us.json b/src/main/resources/assets/modernfix/lang/en_us.json index 8b1b7ad4..b757242b 100644 --- a/src/main/resources/assets/modernfix/lang/en_us.json +++ b/src/main/resources/assets/modernfix/lang/en_us.json @@ -1,5 +1,6 @@ { "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.", "asynclocator.map.locating": "Map (Locating...)", "asynclocator.map.none": "Map (No nearby feature found)" } diff --git a/src/main/resources/assets/modernfix/lang/zh_cn.json b/src/main/resources/assets/modernfix/lang/zh_cn.json index 0c9c92a5..f8e78167 100644 --- a/src/main/resources/assets/modernfix/lang/zh_cn.json +++ b/src/main/resources/assets/modernfix/lang/zh_cn.json @@ -1,5 +1,6 @@ { - "modernfix.jei_load": "正在加载JEI,这需要一点时间", - "asynclocator.map.locating": "地图(正在定位...)", + "modernfix.jei_load": "正在加载JEI,这需要一点时间。", + "modernfix.no_lazydfu": "现代化修复检测到DFU规则在游戏启动时已被编译。这会降低游戏启动速度。非常推荐安装DFU载入优化来解决这个问题。", + "asynclocator.map.locating": "地图(定位中……)", "asynclocator.map.none": "地图(未能找到相关地物)" } diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index baf3a38f..41647d5c 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -33,6 +33,7 @@ ], "client": [ "core.MinecraftMixin", + "core.SynchedEntityDataMixin", "feature.measure_time.MinecraftMixin", "feature.reduce_loading_screen_freezes.ModelBakeryMixin", "bugfix.concurrency.MinecraftMixin", @@ -57,6 +58,7 @@ "perf.cache_model_materials.VanillaModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", + "perf.use_integrated_resources.LootTableHelperMixin", "perf.faster_singleplayer_load.MinecraftServerMixin" ], "injectors": {