diff --git a/build.gradle.kts b/build.gradle.kts index 8fce86fa..262c2aab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -320,8 +320,8 @@ dependencies { shade(group = "io.github.steelwoolmc", name = "mixin-transmogrifier", version = versionMixinTransmog) adapterData(group = "org.sinytra.adapter", name = "adapter", version = versionAdapter) - annotationProcessor(group = "net.fabricmc", name = "sponge-mixin", version = versionMixin) - compileOnly(group = "net.fabricmc", name = "sponge-mixin", version = versionMixin) + annotationProcessor(group = "org.sinytra", name = "sponge-mixin", version = versionMixin) + compileOnly(group = "org.sinytra", name = "sponge-mixin", version = versionMixin) implementation(jarJar("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")!!) { jarJar.ranged(this, "[${mixinextrasVersion},)") } diff --git a/gradle.properties b/gradle.properties index edd5d3b1..3c10d0c1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,18 +4,18 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=true # Versions -versionConnector=1.0.0-beta.40 -versionAdapter=1.11.32-1.20.1-20240314.234555 -versionAdapterDefinition=1.11.34 +versionConnector=1.0.0-beta.41 +versionAdapter=1.11.39-1.20.1-20240406.124555 +versionAdapterDefinition=1.11.39 versionMc=1.20.1 versionForge=47.1.3 versionForgeAutoRenamingTool=1.0.11 versionFabricLoader=2.7.2+0.15.3+1.20.1 versionAccessWidener=2.1.0 -versionFabricApi=0.92.0+1.11.2+1.20.1 -versionMixin=0.12.5+mixin.0.8.5 -versionMixinTransmog=0.4.3+1.20.1 +versionFabricApi=0.92.0+1.11.5+1.20.1 +versionMixin=0.12.10+mixin.0.8.5 +versionMixinTransmog=0.4.5+1.20.1 # Publishing curseForgeId=890127 diff --git a/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorLocator.java b/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorLocator.java index 644df967..ae519a56 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorLocator.java +++ b/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorLocator.java @@ -1,5 +1,6 @@ package dev.su5ed.sinytra.connector.locator; +import com.electronwill.nightconfig.core.file.FileConfig; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.mojang.logging.LogUtils; @@ -8,6 +9,7 @@ import dev.su5ed.sinytra.connector.loader.ConnectorEarlyLoader; import dev.su5ed.sinytra.connector.loader.ConnectorLoaderModMetadata; import dev.su5ed.sinytra.connector.transformer.jar.JarTransformer; +import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.impl.metadata.NestedJarEntry; import net.minecraftforge.fml.loading.ClasspathLocatorUtils; import net.minecraftforge.fml.loading.EarlyLoadingException; @@ -19,6 +21,7 @@ import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileModProvider; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModJarMetadata; +import net.minecraftforge.fml.loading.moddiscovery.NightConfigWrapper; import net.minecraftforge.fml.loading.progress.StartupNotificationManager; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.locating.IDependencyLocator; @@ -60,6 +63,7 @@ public class ConnectorLocator extends AbstractJarFileModProvider implements IDependencyLocator { private static final String NAME = "connector_locator"; private static final String SUFFIX = ".jar"; + private static final String PLACEHOLDER_PROPERTY = "connector:placeholder"; private static final Logger LOGGER = LogUtils.getLogger(); private static final MethodHandle MJM_INIT = uncheck(() -> MethodHandles.privateLookupIn(ModJarMetadata.class, MethodHandles.lookup()).findConstructor(ModJarMetadata.class, MethodType.methodType(void.class))); @@ -86,15 +90,21 @@ public List scanMods(Iterable loadedMods) { return List.of(); } - private List locateFabricMods(Iterable loadedMods) { + private List locateFabricMods(Iterable discoveredMods) { LOGGER.debug(SCAN, "Scanning mods dir {} for mods", FMLPaths.MODSDIR.get()); Path tempDir = ConnectorUtil.CONNECTOR_FOLDER.resolve("temp"); // Get all existing mod ids - Collection loadedModInfos = StreamSupport.stream(loadedMods.spliterator(), false) + Collection loadedModInfos = StreamSupport.stream(discoveredMods.spliterator(), false) .flatMap(modFile -> Optional.ofNullable(modFile.getModFileInfo()).stream()) .flatMap(modFileInfo -> { IModFile modFile = modFileInfo.getFile(); List modInfos = modFileInfo.getMods(); + // Ignore placeholder mods + if (modFileInfo.getFileProperties().containsKey(PLACEHOLDER_PROPERTY)) { + // Set mod version to 0.0 to prioritize the Fabric mod when FML resolves duplicates + modInfos.forEach(mod -> mod.getVersion().parseVersion("0.0")); + return Stream.empty(); + } if (!modInfos.isEmpty()) { return modInfos.stream().map(modInfo -> new SimpleModInfo(modInfo.getModId(), modInfo.getVersion(), false, modFile)); } @@ -102,14 +112,15 @@ private List locateFabricMods(Iterable loadedMods) { return Stream.of(new SimpleModInfo(modFileInfo.moduleName(), new DefaultArtifactVersion(version), true, modFile)); }) .toList(); + Collection loadedModFiles = loadedModInfos.stream().map(SimpleModInfo::origin).toList(); Collection loadedModIds = loadedModInfos.stream().filter(mod -> !mod.library()).map(SimpleModInfo::modid).collect(Collectors.toUnmodifiableSet()); // Discover fabric mod jars List excluded = ModDirTransformerDiscoverer.allExcluded(); List discoveredJars = Stream.of(scanModsDir(excluded), scanClasspath(), scanFromArguments(excluded)).flatMap(s -> s) .map(rethrowFunction(p -> cacheTransformableJar(p.toFile()))) .filter(jar -> { - String modid = jar.modPath().metadata().modMetadata().getId(); - return !shouldIgnoreMod(modid, loadedModIds); + ConnectorLoaderModMetadata metadata = jar.modPath().metadata().modMetadata(); + return !shouldIgnoreMod(metadata, loadedModIds); }) .toList(); Multimap parentToChildren = HashMultimap.create(); @@ -117,7 +128,7 @@ private List locateFabricMods(Iterable loadedMods) { List discoveredNestedJars = discoveredJars.stream() .flatMap(jar -> { ConnectorLoaderModMetadata metadata = jar.modPath().metadata().modMetadata(); - return shouldIgnoreMod(metadata.getId(), loadedModIds) ? Stream.empty() : discoverNestedJarsRecursive(tempDir, jar, metadata.getJars(), parentToChildren, loadedModIds); + return shouldIgnoreMod(metadata, loadedModIds) ? Stream.empty() : discoverNestedJarsRecursive(tempDir, jar, metadata.getJars(), parentToChildren, loadedModIds); }) .toList(); // Collect mods that are (likely) going to be excluded by FML's UniqueModListBuilder. Exclude them from global split package filtering @@ -125,11 +136,11 @@ private List locateFabricMods(Iterable loadedMods) { // Remove mods loaded by FML List uniqueJars = handleDuplicateMods(discoveredJars, discoveredNestedJars, loadedModInfos, ignoredModFiles); // Ensure we have all required dependencies before transforming - List candidates = DependencyResolver.resolveDependencies(uniqueJars, parentToChildren, loadedMods); + List candidates = DependencyResolver.resolveDependencies(uniqueJars, parentToChildren, loadedModFiles); // Get renamer library classpath - List renameLibs = StreamSupport.stream(loadedMods.spliterator(), false).map(modFile -> modFile.getSecureJar().getRootPath()).toList(); + List renameLibs = loadedModFiles.stream().map(modFile -> modFile.getSecureJar().getRootPath()).toList(); // Run jar transformations (or get existing outputs from cache) - List transformed = JarTransformer.transform(candidates, renameLibs, loadedMods); + List transformed = JarTransformer.transform(candidates, renameLibs, loadedModFiles); // Skip last step to save time if an error occured during transformation if (ConnectorEarlyLoader.hasEncounteredException()) { StartupNotificationManager.addModMessage("JAR TRANSFORMATION ERROR"); @@ -137,7 +148,7 @@ private List locateFabricMods(Iterable loadedMods) { return List.of(); } // Deal with split packages (thanks modules) - List moduleSafeJars = SplitPackageMerger.mergeSplitPackages(transformed, loadedMods, ignoredModFiles); + List moduleSafeJars = SplitPackageMerger.mergeSplitPackages(transformed, loadedModFiles, ignoredModFiles); List modFiles = new ArrayList<>(moduleSafeJars.stream().map(this::createConnectorModFile).toList()); // Create mod file for generated adapter mixins jar @@ -154,9 +165,9 @@ private Stream scanModsDir(List excluded) { private Stream filterPaths(Stream stream, List excluded) { return stream - .filter(p -> !excluded.contains(p) && StringUtils.toLowerCase(p.getFileName().toString()).endsWith(SUFFIX)) - .sorted(Comparator.comparing(path -> StringUtils.toLowerCase(path.getFileName().toString()))) - .filter(ConnectorLocator::isFabricModJar); + .filter(p -> !excluded.contains(p) && StringUtils.toLowerCase(p.getFileName().toString()).endsWith(SUFFIX)) + .sorted(Comparator.comparing(path -> StringUtils.toLowerCase(path.getFileName().toString()))) + .filter(ConnectorLocator::isFabricModJar); } private Stream scanClasspath() { @@ -190,7 +201,8 @@ private Stream scanFromArguments(List excluded) { Arrays.stream(paths).filter(s -> !s.isBlank()).map(Path::of).forEach(path -> { if (Files.isDirectory(path)) { uncheck(() -> Files.list(path)).forEach(files::add); - } else { + } + else { files.add(path); } }); @@ -216,7 +228,8 @@ private IModFile createModOrThrow(Path... paths) { private static boolean isFabricModJar(Path path) { SecureJar secureJar = SecureJar.from(path); String name = secureJar.name(); - if (secureJar.moduleDataProvider().findFile(ConnectorUtil.MODS_TOML).isPresent()) { + Path modsToml = secureJar.getPath(ConnectorUtil.MODS_TOML); + if (Files.exists(modsToml) && !containsPlaceholder(modsToml)) { LOGGER.debug(SCAN, "Skipping jar {} as it contains a mods.toml file", path); return false; } @@ -228,6 +241,21 @@ private static boolean isFabricModJar(Path path) { return false; } + private static boolean containsPlaceholder(Path modsTomlPath) { + try { + FileConfig fileConfig = FileConfig.of(modsTomlPath); + fileConfig.load(); + fileConfig.close(); + NightConfigWrapper config = new NightConfigWrapper(fileConfig); + return config.>getConfigElement("properties") + .map(map -> map.containsKey(PLACEHOLDER_PROPERTY)) + .orElse(false); + } catch (Throwable t) { + LOGGER.error("Error reading placeholder information from {}", modsTomlPath, t); + return false; + } + } + private static Stream discoverNestedJarsRecursive(Path tempDir, JarTransformer.TransformableJar parent, Collection jars, Multimap parentToChildren, Collection loadedModIds) { SecureJar secureJar = SecureJar.from(parent.input().toPath()); return jars.stream() @@ -236,7 +264,7 @@ private static Stream discoverNestedJarsRecursi .flatMap(path -> { JarTransformer.TransformableJar jar = uncheck(() -> prepareNestedJar(tempDir, secureJar.getPrimaryPath().getFileName().toString(), path)); ConnectorLoaderModMetadata metadata = jar.modPath().metadata().modMetadata(); - if (shouldIgnoreMod(metadata.getId(), loadedModIds)) { + if (shouldIgnoreMod(metadata, loadedModIds)) { return Stream.empty(); } parentToChildren.put(parent, jar); @@ -289,7 +317,9 @@ private static List handleDuplicateMods(List loadedModIds) { + private static boolean shouldIgnoreMod(ConnectorLoaderModMetadata metadata, Collection loadedModIds) { + if (!metadata.loadsInEnvironment(FabricLoader.getInstance().getEnvironmentType())) return true; + String id = metadata.getId(); return ConnectorUtil.DISABLED_MODS.contains(id) || loadedModIds.contains(id); } diff --git a/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorModMetadataParser.java b/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorModMetadataParser.java index c1cd08b5..1a3b9494 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorModMetadataParser.java +++ b/src/main/java/dev/su5ed/sinytra/connector/locator/ConnectorModMetadataParser.java @@ -23,6 +23,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +/** + * Parses Fabric mod JSON metadata into TOML format at runtime. + */ public final class ConnectorModMetadataParser { private static final String DEFAULT_LICENSE = "All Rights Reserved"; // From ModInfo diff --git a/src/main/java/dev/su5ed/sinytra/connector/locator/EmbeddedDependencies.java b/src/main/java/dev/su5ed/sinytra/connector/locator/EmbeddedDependencies.java index 9f3d663a..5070dd94 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/locator/EmbeddedDependencies.java +++ b/src/main/java/dev/su5ed/sinytra/connector/locator/EmbeddedDependencies.java @@ -3,6 +3,7 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.mojang.logging.LogUtils; +import net.minecraftforge.fml.loading.FMLEnvironment; import org.apache.commons.lang3.RandomStringUtils; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -55,7 +56,7 @@ public final class EmbeddedDependencies { // Return a random string to still write an input file, so that once we have a proper version available we refresh the cache return RandomStringUtils.randomAlphabetic(5); } - return ver; + return ver + "," + FMLEnvironment.dist.name().toLowerCase(); }); /** diff --git a/src/main/java/dev/su5ed/sinytra/connector/service/ConnectorLoaderService.java b/src/main/java/dev/su5ed/sinytra/connector/service/ConnectorLoaderService.java index b89b77a5..00acb461 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/service/ConnectorLoaderService.java +++ b/src/main/java/dev/su5ed/sinytra/connector/service/ConnectorLoaderService.java @@ -39,6 +39,11 @@ public class ConnectorLoaderService implements ITransformationService { private static final String AUTHLIB_MODULE = "authlib"; private static final Logger LOGGER = LogUtils.getLogger(); + static { + // Enable frame expansion fixes on our mixin fork + System.setProperty("mixin.env.fixFrameExpansion", "true"); + } + @Override public String name() { return NAME; diff --git a/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java b/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java index 7413d520..89bb2e6d 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java +++ b/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java @@ -86,10 +86,15 @@ static void apply(Map configToModMap) { config.decorate(FabricUtil.KEY_MOD_ID, mod.moduleName()); if (!mod.getMods().isEmpty()) { String modid = mod.getMods().get(0).getModId(); - int compat = ConnectorEarlyLoader.isConnectorMod(modid) ? FabricLoaderImpl.INSTANCE.getModContainer(modid) - .map(m -> getMixinCompat(m.getMetadata())) - .orElse(FabricUtil.COMPATIBILITY_0_10_0) - : FabricUtil.COMPATIBILITY_0_10_0; + int compat; + if (ConnectorEarlyLoader.isConnectorMod(modid)) { + compat = FabricLoaderImpl.INSTANCE.getModContainer(modid) + .map(m -> getMixinCompat(m.getMetadata())) + .orElse(FabricUtil.COMPATIBILITY_0_10_0); + config.decorate(FabricUtil.KEY_FIX_FRAME_EXPANSION, true); + } else { + compat = FabricUtil.COMPATIBILITY_0_10_0; + } config.decorate(FabricUtil.KEY_COMPATIBILITY, compat); } } diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java index 42c9a819..b65f7a8d 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java @@ -14,13 +14,14 @@ import org.sinytra.adapter.patch.api.MixinConstants; import org.sinytra.adapter.patch.api.Patch; import org.sinytra.adapter.patch.transformer.ModifyMethodAccess; -import org.sinytra.adapter.patch.transformer.ModifyMethodParams; +import org.sinytra.adapter.patch.transformer.param.ParamTransformTarget; import java.util.List; import java.util.ListIterator; import java.util.stream.Collectors; import java.util.stream.Stream; +@SuppressWarnings("deprecation") public class MixinPatches { public static List getPatches() { final List patches = List.of(Patch.builder() @@ -116,7 +117,7 @@ public static List getPatches() { .insert(0, Type.getObjectType("net/minecraft/world/item/ItemStack")) .insert(1, Type.getObjectType("net/minecraft/world/item/ItemStack")) .insert(2, Type.getObjectType("net/minecraft/world/entity/LivingEntity")) - .targetType(ModifyMethodParams.TargetType.INJECTION_POINT)) + .targetType(ParamTransformTarget.INJECTION_POINT)) .build(), Patch.builder() .targetClass("net/minecraft/world/item/MilkBucketItem") @@ -168,7 +169,7 @@ public static List getPatches() { .targetMixinType(MixinConstants.REDIRECT) .modifyInjectionPoint("Lnet/minecraft/world/entity/vehicle/Boat;canBoatInFluid(Lnet/minecraft/world/level/material/FluidState;)Z") .modifyParams(b -> b - .targetType(ModifyMethodParams.TargetType.INJECTION_POINT) + .targetType(ParamTransformTarget.INJECTION_POINT) .ignoreOffset() .insert(0, Type.getObjectType("net/minecraft/world/entity/vehicle/Boat")) .inline(2, i -> i.getstatic("net/minecraft/tags/FluidTags", "f_13131_", "Lnet/minecraft/tags/TagKey;"))) @@ -211,7 +212,7 @@ public static List getPatches() { .modifyTarget("tryCatchFire") .modifyParams(b -> b .insert(5, Type.getObjectType("net/minecraft/core/Direction")) - .targetType(ModifyMethodParams.TargetType.METHOD)) + .targetType(ParamTransformTarget.METHOD)) .build(), Patch.builder() .targetClass("net/minecraft/world/level/block/FireBlock") @@ -224,7 +225,7 @@ public static List getPatches() { .targetInjectionPoint("Lnet/minecraft/client/gui/Gui;m_168688_(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/entity/player/Player;IIIIFIIIZ)V") .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget("renderHealth(IILnet/minecraft/client/gui/GuiGraphics;)V") - .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .build(), Patch.builder() .targetClass("net/minecraft/client/gui/Gui") @@ -248,7 +249,7 @@ public static List getPatches() { .targetInjectionPoint("Lnet/minecraft/util/profiling/ProfilerFiller;m_6182_(Ljava/lang/String;)V") .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget("renderFood(IILnet/minecraft/client/gui/GuiGraphics;)V") - .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .modifyInjectionPoint("HEAD", "", true) .build(), Patch.builder() @@ -258,7 +259,7 @@ public static List getPatches() { .targetAnnotationValues(h -> h.getNested("at").flatMap(v -> v.>getValue("args").map(a -> a.get().get(0).equals("intValue=-10"))).orElse(false)) .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget("renderFood(IILnet/minecraft/client/gui/GuiGraphics;)V") - .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .modifyInjectionPoint("INVOKE", "Lcom/mojang/blaze3d/systems/RenderSystem;disableBlend()V") .build(), Patch.builder() @@ -272,7 +273,7 @@ public static List getPatches() { .targetMethod("m_280173_(Lnet/minecraft/client/gui/GuiGraphics;)V") .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget("renderArmor(Lnet/minecraft/client/gui/GuiGraphics;II)V") - .modifyParams(b -> b.insert(1, Type.INT_TYPE).insert(2, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(1, Type.INT_TYPE).insert(2, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .build(), Patch.builder() .targetClass("net/minecraft/client/gui/Gui") @@ -301,7 +302,7 @@ public static List getPatches() { .targetInjectionPoint("HEAD", "") .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget("renderHealthMount(IILnet/minecraft/client/gui/GuiGraphics;)V") - .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .build(), Patch.builder() .targetClass("net/minecraft/world/entity/player/Player") @@ -420,9 +421,7 @@ public static List getPatches() { .targetClass("net/minecraft/server/level/ServerPlayerGameMode") .targetMethod("m_9280_") .targetInjectionPoint("Lnet/minecraft/world/level/block/Block;m_5707_(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)V") - .modifyTarget("removeBlock") - .modifyParams(builder -> builder.insert(1, Type.BOOLEAN_TYPE)) - .modifyInjectionPoint("Lnet/minecraft/world/level/block/state/BlockState;onDestroyedByPlayer(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;ZLnet/minecraft/world/level/material/FluidState;)Z") + .modifyInjectionPoint("Lnet/minecraft/server/level/ServerPlayerGameMode;removeBlock(Lnet/minecraft/core/BlockPos;Z)Z") .build(), Patch.builder() .targetClass("net/minecraft/server/level/ServerPlayerGameMode") @@ -463,7 +462,7 @@ public static List getPatches() { .modifyParams(builder -> builder .insert(0, Type.getObjectType("net/minecraft/client/renderer/entity/layers/ElytraLayer")) .replace(2, Type.getObjectType("net/minecraft/world/entity/LivingEntity")) - .targetType(ModifyMethodParams.TargetType.INJECTION_POINT) + .targetType(ParamTransformTarget.INJECTION_POINT) .ignoreOffset()) .divertRedirector(adapter -> { adapter.visitVarInsn(Opcodes.ALOAD, 1); @@ -519,7 +518,7 @@ public static List getPatches() { .targetInjectionPoint("INVOKE", "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;m_289609_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V") .modifyInjectionPoint("Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/Model;ZFFFLnet/minecraft/resources/ResourceLocation;)V") .modifyParams(builder -> builder - .targetType(ModifyMethodParams.TargetType.ALL) + .targetType(ParamTransformTarget.ALL) .replace(0, Type.getObjectType("net/minecraft/client/model/Model")) .lvtFixer((index, insn, list) -> { if (index == 1) { @@ -587,7 +586,7 @@ private static Patch buildGuiPatch(int minOrdinal, int maxOrdinal, String target .targetAnnotationValues(h -> h.getNested("at").flatMap(v -> v.getValue("ordinal")).map(v -> v.get() >= minOrdinal && v.get() <= maxOrdinal).orElse(false)) .extractMixin("net/minecraftforge/client/gui/overlay/ForgeGui") .modifyTarget(targetMethodName + "(IILnet/minecraft/client/gui/GuiGraphics;)V") - .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ModifyMethodParams.TargetType.METHOD)) + .modifyParams(b -> b.insert(0, Type.INT_TYPE).insert(1, Type.INT_TYPE).targetType(ParamTransformTarget.METHOD)) .transform((classNode, methodNode, methodContext, context) -> { methodContext.injectionPointAnnotation() .getValue("ordinal") diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/OptimizedRenamingTransformer.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/OptimizedRenamingTransformer.java index e01e3eac..44412a4b 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/OptimizedRenamingTransformer.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/OptimizedRenamingTransformer.java @@ -2,7 +2,6 @@ import dev.su5ed.sinytra.connector.transformer.jar.IntermediateMapping; import net.minecraftforge.fart.api.ClassProvider; -import net.minecraftforge.fart.api.Transformer; import net.minecraftforge.fart.internal.ClassProviderImpl; import net.minecraftforge.fart.internal.EnhancedClassRemapper; import net.minecraftforge.fart.internal.EnhancedRemapper; @@ -27,6 +26,7 @@ import org.objectweb.asm.tree.MethodNode; import org.sinytra.adapter.patch.selector.AnnotationHandle; import org.sinytra.adapter.patch.util.MethodQualifier; +import org.spongepowered.asm.mixin.gen.AccessorInfo; import java.io.IOException; import java.util.Collection; @@ -37,20 +37,23 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Stream; public final class OptimizedRenamingTransformer extends RenamingTransformer { private static final String CLASS_DESC_PATTERN = "^L[a-zA-Z0-9/$_]+;$"; - private static final String FQN_CLASS_NAME_PATTERN = "^([a-zA-Z0-9$_]+\\.)*[a-zA-Z0-9$_]+$"; + private static final String FQN_CLASS_NAME_PATTERN = "^(?:[a-zA-Z0-9$_]+\\.)*[a-zA-Z0-9$_]+$"; + private static final String INTERNAL_CLASS_NAME_PATTERN = "^(?:[a-zA-Z0-9$_]+/)*[a-zA-Z0-9$_]+$"; + private static final Pattern FIELD_QUALIFIER_PATTERN = Pattern.compile("^(?L[\\\\\\w/$]+;)?(?\\w+)(?::(?\\[*[ZCBSIFJD]|\\[*L[a-zA-Z0-9/_$]+;))?$"); + private static final String ACCESSOR_METHOD_PATTERN = "^.*(Method_|Field_|Comp_).*$"; - public static Transformer create(ClassProvider classProvider, Consumer log, IMappingFile mappingFile, IntermediateMapping flatMappings) { - IntermediaryClassProvider reverseProvider = new IntermediaryClassProvider(classProvider, mappingFile, mappingFile.reverse(), log); - EnhancedRemapper enhancedRemapper = new MixinAwareEnhancedRemapper(reverseProvider, mappingFile, flatMappings, log); - return new OptimizedRenamingTransformer(enhancedRemapper, false); - } + private final boolean remapRefs; - public OptimizedRenamingTransformer(EnhancedRemapper remapper, boolean collectAbstractParams) { + public OptimizedRenamingTransformer(EnhancedRemapper remapper, boolean collectAbstractParams, boolean remapRefs) { super(remapper, collectAbstractParams); + + this.remapRefs = remapRefs; } @Override @@ -66,11 +69,16 @@ protected void postProcess(ClassNode node) { postProcessRemapper.mapAnnotationValues(annotation.values); } } + if (node.invisibleAnnotations != null) { + for (AnnotationNode annotation : node.invisibleAnnotations) { + postProcessRemapper.mapAnnotationValues(annotation.values); + } + } for (MethodNode method : node.methods) { if (method.visibleAnnotations != null) { - // If remap has been set to false during compilation, we must manually map the annotation values ourselves instead of relying on the provided refmap - if (method.visibleAnnotations.stream().anyMatch(ann -> new AnnotationHandle(ann).getValue("remap").map(h -> !h.get()).orElse(false))) { - for (AnnotationNode annotation : method.visibleAnnotations) { + for (AnnotationNode annotation : method.visibleAnnotations) { + // If remap has been set to false during compilation, we must manually map the annotation values ourselves instead of relying on the provided refmap + if (this.remapRefs || new AnnotationHandle(annotation).getValue("remap").map(h -> !h.get()).orElse(false)) { postProcessRemapper.mapAnnotationValues(annotation.values); } } @@ -117,17 +125,23 @@ else if (obj instanceof List list) { public Object mapValue(Object value) { if (value instanceof String str) { if (str.matches(CLASS_DESC_PATTERN)) { - String mapped = flatMappings.map(str.substring(1, str.length() - 1)); + String mapped = this.flatMappings.map(str.substring(1, str.length() - 1)); if (mapped != null) { return 'L' + mapped + ';'; } } else if (str.matches(FQN_CLASS_NAME_PATTERN)) { - String mapped = flatMappings.map(str.replace('.', '/')); + String mapped = this.flatMappings.map(str.replace('.', '/')); if (mapped != null) { return mapped.replace('/', '.'); } } + else if (str.matches(INTERNAL_CLASS_NAME_PATTERN)) { + String mapped = this.flatMappings.map(str); + if (mapped != null) { + return mapped; + } + } MethodQualifier qualifier = MethodQualifier.create(str).orElse(null); if (qualifier != null && qualifier.desc() != null) { @@ -136,6 +150,21 @@ else if (str.matches(FQN_CLASS_NAME_PATTERN)) { String desc = this.remapper.mapMethodDesc(qualifier.desc()); return owner + name + desc; } + else { + Matcher fieldMatcher = FIELD_QUALIFIER_PATTERN.matcher(str); + if (fieldMatcher.matches()) { + String owner = fieldMatcher.group("owner"); + String name = fieldMatcher.group("name"); + String desc = fieldMatcher.group("desc"); + + if (owner != null || name != null && (name.startsWith("field_") || name.startsWith("comp_"))) { + String mappedOwner = owner != null ? this.remapper.mapDesc(owner) : ""; + String mappedName = name != null ? this.flatMappings.mapField(name, desc != null ? desc : "") : ""; + + return mappedOwner + mappedName + (desc != null ? ":" + this.remapper.mapDesc(desc) : ""); + } + } + } String mapped = this.flatMappings.map(str); if (mapped != null) { @@ -146,14 +175,14 @@ else if (str.matches(FQN_CLASS_NAME_PATTERN)) { } } - private static final class IntermediaryClassProvider implements ClassProvider { + public static final class IntermediaryClassProvider implements ClassProvider { private final ClassProvider upstream; private final IMappingFile forwardMapping; private final EnhancedRemapper remapper; private final Map> classCache = new ConcurrentHashMap<>(); - private IntermediaryClassProvider(ClassProvider upstream, IMappingFile forwardMapping, IMappingFile reverseMapping, Consumer log) { + public IntermediaryClassProvider(ClassProvider upstream, IMappingFile forwardMapping, IMappingFile reverseMapping, Consumer log) { this.upstream = upstream; this.forwardMapping = forwardMapping; this.remapper = new EnhancedRemapper(upstream, reverseMapping, log); @@ -191,7 +220,7 @@ public void close() throws IOException { } } - private static class MixinAwareEnhancedRemapper extends EnhancedRemapper { + public static class MixinAwareEnhancedRemapper extends EnhancedRemapper { private final IntermediateMapping flatMappings; public MixinAwareEnhancedRemapper(ClassProvider classProvider, IMappingFile map, IntermediateMapping flatMappings, Consumer log) { @@ -246,6 +275,15 @@ public String mapMethodName(String owner, String name, String descriptor) { String mapped = fastMappedLambda != null ? fastMappedLambda : mapMethodName(owner, actualName, descriptor); return name.substring(0, interfacePrefix + 1) + mapped; } + if (name.matches(ACCESSOR_METHOD_PATTERN)) { + AccessorInfo.AccessorName accessorName = AccessorInfo.AccessorName.of(name); + if (accessorName != null) { + String mapped = this.flatMappings.mapMethod(accessorName.name, descriptor); + if (mapped != null) { + return accessorName.prefix + mapped.substring(0, 1).toUpperCase() + mapped.substring(1); + } + } + } } return null; }) diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java index fca33e41..ac2d089d 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java @@ -5,14 +5,6 @@ import com.google.gson.JsonElement; import com.mojang.logging.LogUtils; import com.mojang.serialization.JsonOps; -import dev.su5ed.sinytra.connector.transformer.patch.ReflectionRenamingTransformer; -import org.sinytra.adapter.patch.LVTOffsets; -import org.sinytra.adapter.patch.api.GlobalReferenceMapper; -import org.sinytra.adapter.patch.api.Patch; -import org.sinytra.adapter.patch.api.PatchEnvironment; -import org.sinytra.adapter.patch.serialization.PatchSerialization; -import org.sinytra.adapter.patch.util.provider.ClassLookup; -import org.sinytra.adapter.patch.util.provider.ZipClassLookup; import dev.su5ed.sinytra.connector.locator.EmbeddedDependencies; import dev.su5ed.sinytra.connector.service.FabricMixinBootstrap; import dev.su5ed.sinytra.connector.transformer.AccessWidenerTransformer; @@ -27,17 +19,25 @@ import dev.su5ed.sinytra.connector.transformer.patch.ClassAnalysingTransformer; import dev.su5ed.sinytra.connector.transformer.patch.ClassNodeTransformer; import dev.su5ed.sinytra.connector.transformer.patch.ConnectorRefmapHolder; +import dev.su5ed.sinytra.connector.transformer.patch.ReflectionRenamingTransformer; import net.fabricmc.loader.impl.FabricLoaderImpl; import net.fabricmc.loader.impl.MappingResolverImpl; import net.minecraftforge.coremod.api.ASMAPI; import net.minecraftforge.fart.api.ClassProvider; import net.minecraftforge.fart.api.Renamer; -import net.minecraftforge.fart.api.Transformer; +import net.minecraftforge.fart.internal.EnhancedRemapper; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.targets.CommonLaunchHandler; import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.srgutils.IMappingFile; +import org.sinytra.adapter.patch.LVTOffsets; +import org.sinytra.adapter.patch.api.GlobalReferenceMapper; +import org.sinytra.adapter.patch.api.Patch; +import org.sinytra.adapter.patch.api.PatchEnvironment; +import org.sinytra.adapter.patch.serialization.PatchSerialization; +import org.sinytra.adapter.patch.util.provider.ClassLookup; +import org.sinytra.adapter.patch.util.provider.ZipClassLookup; import org.slf4j.Logger; import java.io.File; @@ -66,7 +66,7 @@ public class JarTransformInstance { private final List adapterPatches; private final LVTOffsets lvtOffsetsData; private final BytecodeFixerUpperFrontend bfu; - private final Transformer remappingTransformer; + private final EnhancedRemapper enhancedRemapper; private final ClassLookup cleanClassLookup; private final List libs; @@ -94,7 +94,9 @@ public JarTransformInstance(ClassProvider classProvider, Iterable load } this.bfu = new BytecodeFixerUpperFrontend(); - this.remappingTransformer = OptimizedRenamingTransformer.create(classProvider, s -> {}, FabricLoaderImpl.INSTANCE.getMappingResolver().getCurrentMap(SOURCE_NAMESPACE), IntermediateMapping.get(SOURCE_NAMESPACE)); + IMappingFile mappingFile = FabricLoaderImpl.INSTANCE.getMappingResolver().getCurrentMap(SOURCE_NAMESPACE); + ClassProvider intermediaryClassProvider = new OptimizedRenamingTransformer.IntermediaryClassProvider(classProvider, mappingFile, mappingFile.reverse(), s -> {}); + this.enhancedRemapper = new OptimizedRenamingTransformer.MixinAwareEnhancedRemapper(intermediaryClassProvider, mappingFile, IntermediateMapping.get(SOURCE_NAMESPACE), s -> {}); this.cleanClassLookup = createCleanClassLookup(); this.libs = libs; @@ -137,7 +139,7 @@ public void transformJar(File input, Path output, FabricModFileMetadata metadata accessorRedirectTransformer, new ReflectionRenamingTransformer(intermediaryToSrg, IntermediateMapping.get(SOURCE_NAMESPACE)) )) - .add(this.remappingTransformer) + .add(new OptimizedRenamingTransformer(this.enhancedRemapper, false, metadata.refmaps().isEmpty())) .add(new ClassNodeTransformer(new ClassAnalysingTransformer())) .add(patchTransformer) .add(refmapRemapper) diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformer.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformer.java index 50429d32..089e16d5 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformer.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformer.java @@ -25,6 +25,7 @@ import org.slf4j.Marker; import org.slf4j.MarkerFactory; import org.spongepowered.asm.launch.MixinLaunchPluginLegacy; +import org.spongepowered.asm.mixin.transformer.ClassInfo; import org.spongepowered.asm.service.MixinService; import java.io.File; @@ -42,6 +43,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -167,7 +169,7 @@ private static List transformJars(List paths, L } catch (InterruptedException ignored) { return List.of(); } finally { - setMixinClassProvider(null); + cleanupEnvironment(); progress.complete(); } } @@ -239,6 +241,31 @@ private static void readMixinConfigPackages(File input, JarFile jarFile, ZipEntr } } + @SuppressWarnings("unchecked") + private static void cleanupEnvironment() { + setMixinClassProvider(null); + try { + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(ClassInfo.class, MethodHandles.lookup()); + + // Remove cached class infos, which might not be up-to-date + VarHandle cacheField = lookup.findStaticVarHandle(ClassInfo.class, "cache", Map.class); + Map cache = (Map) cacheField.get(); + cache.clear(); + + VarHandle nonExpandedCacheField = lookup.findStaticVarHandle(ClassInfo.class, "nonExpandedCache", Map.class); + Map nonExpandedCache = (Map) nonExpandedCacheField.get(); + nonExpandedCache.clear(); + + VarHandle objectField = lookup.findStaticVarHandle(ClassInfo.class, "OBJECT", ClassInfo.class); + ClassInfo object = (ClassInfo) objectField.get(); + + cache.put("java/lang/Object", object); + nonExpandedCache.put("java/lang/Object", object); + } catch (Throwable t) { + LOGGER.error("Error cleaning up after jar transformation", t); + } + } + private JarTransformer() {} public record FabricModPath(Path path, FabricModFileMetadata metadata) {} diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/patch/ClassAnalysingTransformer.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/patch/ClassAnalysingTransformer.java index f558e360..f1731c2a 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/patch/ClassAnalysingTransformer.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/patch/ClassAnalysingTransformer.java @@ -17,7 +17,10 @@ public class ClassAnalysingTransformer implements ClassNodeTransformer.ClassProc new MethodQualifier("dev/su5ed/sinytra/connector/mod/ConnectorMod", "getModResourceAsStream", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/io/InputStream;"), new MethodQualifier("Lnet/minecraft/world/level/storage/loot/LootDataType;", ASMAPI.mapMethod("m_278763_"), "(Lnet/minecraft/resources/ResourceLocation;Lcom/google/gson/JsonElement;)Ljava/util/Optional;"), - new MethodQualifier("dev/su5ed/sinytra/connector/mod/ConnectorMod", "deserializeLootTable", "(Lnet/minecraft/world/level/storage/loot/LootDataType;Lnet/minecraft/resources/ResourceLocation;Lcom/google/gson/JsonElement;)Ljava/util/Optional;") + new MethodQualifier("dev/su5ed/sinytra/connector/mod/ConnectorMod", "deserializeLootTable", "(Lnet/minecraft/world/level/storage/loot/LootDataType;Lnet/minecraft/resources/ResourceLocation;Lcom/google/gson/JsonElement;)Ljava/util/Optional;"), + + new MethodQualifier("Lcom/electronwill/nightconfig/core/file/FileConfigBuilder;", "defaultResource", "(Ljava/lang/String;)Lcom/electronwill/nightconfig/core/file/GenericBuilder;"), + new MethodQualifier("dev/su5ed/sinytra/connector/mod/ConnectorMod", "useModConfigResource", "(Lcom/electronwill/nightconfig/core/file/FileConfigBuilder;Ljava/lang/String;)Lcom/electronwill/nightconfig/core/file/GenericBuilder;") ); @Override diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorMod.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorMod.java index 6fd4d7d2..80f3abf6 100644 --- a/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorMod.java +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorMod.java @@ -1,5 +1,8 @@ package dev.su5ed.sinytra.connector.mod; +import com.electronwill.nightconfig.core.file.FileConfigBuilder; +import com.electronwill.nightconfig.core.file.FileNotFoundAction; +import com.electronwill.nightconfig.core.file.GenericBuilder; import com.google.gson.JsonElement; import dev.su5ed.sinytra.connector.ConnectorUtil; import dev.su5ed.sinytra.connector.locator.ConnectorConfig; @@ -25,12 +28,17 @@ import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.loading.FMLLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.InputStream; +import java.net.URL; import java.util.Optional; @Mod(ConnectorUtil.CONNECTOR_MODID) public class ConnectorMod { + public static final Logger LOG = LoggerFactory.getLogger(ConnectorMod.class); + private static boolean clientLoadComplete; private static boolean preventFreeze; @@ -82,6 +90,13 @@ public static Optional deserializeLootTable(LootDataType type, Resourc return type.deserialize(location, json, DummyResourceManager.INSTANCE); } + // Injected into mod code by ClassAnalysingTransformer + @SuppressWarnings("unused") + public static GenericBuilder useModConfigResource(FileConfigBuilder builder, String resource) { + URL url = ConnectorMod.class.getClassLoader().getResource(resource); + return builder.onFileNotFound(FileNotFoundAction.copyData(url)); + } + @SuppressWarnings("deprecation") public static void unfreezeRegistries() { ((MappedRegistry) BuiltInRegistries.REGISTRY).unfreeze(); diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/compat/DynamicRegistryPrefixes.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/compat/DynamicRegistryPrefixes.java new file mode 100644 index 00000000..b600f5f9 --- /dev/null +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/compat/DynamicRegistryPrefixes.java @@ -0,0 +1,10 @@ +package dev.su5ed.sinytra.connector.mod.compat; + +import net.fabricmc.fabric.impl.registry.sync.DynamicRegistriesImpl; +import net.minecraft.resources.ResourceKey; + +public class DynamicRegistryPrefixes { + public static boolean isRegisteredFabricDynamicRegistry(ResourceKey key) { + return DynamicRegistriesImpl.FABRIC_DYNAMIC_REGISTRY_KEYS.stream().anyMatch(key::equals); + } +} diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/ForgeHooksMixin.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/ForgeHooksMixin.java index b0e23c62..6da0f8ce 100644 --- a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/ForgeHooksMixin.java +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/ForgeHooksMixin.java @@ -1,14 +1,24 @@ package dev.su5ed.sinytra.connector.mod.mixin; +import com.llamalad7.mixinextras.sugar.Local; +import dev.su5ed.sinytra.connector.ConnectorUtil; +import dev.su5ed.sinytra.connector.mod.ConnectorMod; import dev.su5ed.sinytra.connector.mod.compat.FluidHandlerCompat; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.DefaultAttributes; import net.minecraft.world.level.material.Fluid; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.fluids.FluidType; 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; +import java.util.Map; + @Mixin(ForgeHooks.class) public abstract class ForgeHooksMixin { @@ -19,4 +29,18 @@ private static void getFabricVanillaFluidType(Fluid fluid, CallbackInfoReturnabl cir.setReturnValue(fabricFluidType); } } + + @Inject(at = @At("TAIL"), method = "modifyAttributes", remap = false) + private static void connector$allowAttributeMixins(CallbackInfo ci, @Local Map, AttributeSupplier.Builder> modifiedMap) { + modifiedMap.forEach((entity, attributes) -> { + final var fromVanilla = DefaultAttributes.getSupplier(entity); + // A mod is mixing into DefaultAttributes to add their attribute + if (ForgeHooks.getAttributesView().get(entity) != fromVanilla) { + ConnectorMod.LOG.debug("Entity {} has its attributes added via a mixin. Adding event-modified attributes.", entity); + final AttributeSupplier.Builder newBuilder = new AttributeSupplier.Builder(fromVanilla); + newBuilder.combine(attributes); + fromVanilla.instances = newBuilder.build().instances; + } + }); + } } diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/lang/PathPackResourcesMixin.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/lang/PathPackResourcesMixin.java index 6da9265b..5043e45a 100644 --- a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/lang/PathPackResourcesMixin.java +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/lang/PathPackResourcesMixin.java @@ -1,20 +1,33 @@ package dev.su5ed.sinytra.connector.mod.mixin.lang; import dev.su5ed.sinytra.connector.mod.compat.PathPackResourcesExtensions; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.resources.IoSupplier; import net.minecraftforge.resource.PathPackResources; -import org.objectweb.asm.Opcodes; 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.Redirect; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.InputStream; @Mixin(PathPackResources.class) -public class PathPackResourcesMixin { - @Redirect(method = "getResource", at = @At(value = "FIELD", target = "Lnet/minecraft/server/packs/PackType;CLIENT_RESOURCES:Lnet/minecraft/server/packs/PackType;", opcode = Opcodes.GETSTATIC)) - public PackType modifyResourceType(PackType type) { - if (this instanceof PathPackResourcesExtensions ext && ext.connector_isFabricMod()) { - return type; +public abstract class PathPackResourcesMixin { + @Shadow + private static String[] getPathFromLocation(PackType type, ResourceLocation location) { + throw new UnsupportedOperationException(); + } + + @Inject(method = "getResource", at = @At("HEAD"), cancellable = true) + private void getFabricLangResources(PackType type, ResourceLocation location, CallbackInfoReturnable> cir) { + if (this instanceof PathPackResourcesExtensions ext && ext.connector_isFabricMod() && location.getPath().startsWith("lang/")) { + IoSupplier serverLang = ((PackResources) this).getRootResource(getPathFromLocation(PackType.SERVER_DATA, location)); + if (serverLang != null) { + cir.setReturnValue(serverLang); + } } - return PackType.CLIENT_RESOURCES; } } diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/registries/RegistryDataLoaderMixin.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/registries/RegistryDataLoaderMixin.java index 4d3f1750..66a4f06d 100644 --- a/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/registries/RegistryDataLoaderMixin.java +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/mixin/registries/RegistryDataLoaderMixin.java @@ -1,6 +1,7 @@ package dev.su5ed.sinytra.connector.mod.mixin.registries; import dev.su5ed.sinytra.connector.loader.ConnectorEarlyLoader; +import dev.su5ed.sinytra.connector.mod.compat.DynamicRegistryPrefixes; import net.minecraft.core.Registry; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.RegistryDataLoader; @@ -70,9 +71,13 @@ private static String modifyRegistryDirPath(ResourceLocation location, RegistryO @Unique private static boolean connector$shouldOmitPrefix(ResourceLocation location, ResourceKey> registryKey, ResourceManager manager) { + String modid = location.getNamespace(); + // Fabric mod registries added directly to RegistryDataLoader.WORLDGEN_REGISTRIES should not be prefixed + if (ConnectorEarlyLoader.isConnectorMod(modid) && ModList.get().isLoaded("fabric_registry_sync_v0") && !DynamicRegistryPrefixes.isRegisteredFabricDynamicRegistry(registryKey)) { + return true; + } // Check if the registry has been registered if (DataPackRegistriesHooks.getDataPackRegistries().stream().noneMatch(data -> registryKey.equals(data.key()))) { - String modid = location.getNamespace(); // If the namespace is one of a fabric mod, omit the prefix if (ConnectorEarlyLoader.isConnectorMod(modid)) { return true;