From 8a1c7aaf3d3d219046a1c85a4503d94cf34d14a9 Mon Sep 17 00:00:00 2001 From: XMRhapsody Date: Thu, 29 Oct 2026 14:29:46 +0900 Subject: [PATCH] Add FML 4.0 Vulkan compatibility with runtime FML version detection FmlRuntime reads FishModLoader.VERSION at runtime. On FML 3.x every render mixin keeps its original (legacy OpenGL) route; on FML 4.x with the Vulkan backend each FML native area (special, text, sky, texture, container) is queried through VulkanConfig.areaActive, so an area whose native renderer is switched off falls back to the legacy route. Routed mixins are contributed by IMixinConfigPlugin.getMixins instead of the JSON lists, so exactly one variant is loaded: - RenderBiped/RenderPlayer and glacier GuiContainerCreative overwrites move to *.vulkan.mixins.json (mixinPriority 400) on the native route, so they merge before FML's native redirects (priority 500) - FontRenderer RGB colour, TextureMap atlas aliases, sprite interpolation metadata and the multi-box selection outline get native variants that hook FML's rewritten method bodies Also mark MessageComponentSerializerMixin.writeClickEvent cancellable: setReturnValue threw CancellationException on every /tellraw. --- .../internal/client/chat/RgbTextColor.java | 31 ++++++ .../client/texture/InterpolatedSprite.java | 9 ++ .../texture/LegacyAliasResourceManager.java | 44 ++++++++ .../internal/compat/FmlRuntime.java | 96 +++++++++++++++++ .../internal/compat/NativeGlShim.java | 102 ++++++++++++++++++ .../internal/config/GlacierMixinConfig.java | 12 +++ .../config/GlacierVulkanMixinConfig.java | 12 +++ .../internal/config/MixinConfig.java | 48 +++------ .../internal/config/RoutedMixinPlugin.java | 72 +++++++++++++ .../internal/config/VulkanMixinConfig.java | 14 +++ .../MessageComponentSerializerMixin.java | 2 +- .../mixin/client/FontRendererColorMixin.java | 26 +++++ .../mixin/client/FontRendererMixin.java | 39 ++----- .../render/TextureAtlasSpriteMixin.java | 9 +- .../client/render/TextureMapAtlasMixin.java | 23 ++++ .../mixin/client/render/TextureMapMixin.java | 13 --- .../vulkan/FontRendererVulkanMixin.java | 28 +++++ .../vulkan/RenderGlobalVulkanMixin.java | 56 ++++++++++ .../vulkan/TextureAtlasSpriteVulkanMixin.java | 22 ++++ .../client/vulkan/TextureMapVulkanMixin.java | 21 ++++ src/main/resources/fml.mod.json | 4 +- src/main/resources/glacier.mixins.json | 2 +- src/main/resources/glacier.vulkan.mixins.json | 12 +++ src/main/resources/rustedironcore.mixins.json | 3 - .../rustedironcore.vulkan.mixins.json | 15 +++ 25 files changed, 632 insertions(+), 83 deletions(-) create mode 100644 src/main/java/moddedmite/rustedironcore/internal/client/chat/RgbTextColor.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/client/texture/InterpolatedSprite.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/client/texture/LegacyAliasResourceManager.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/compat/FmlRuntime.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/compat/NativeGlShim.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/config/GlacierMixinConfig.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/config/GlacierVulkanMixinConfig.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/config/RoutedMixinPlugin.java create mode 100644 src/main/java/moddedmite/rustedironcore/internal/config/VulkanMixinConfig.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererColorMixin.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapAtlasMixin.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/FontRendererVulkanMixin.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/RenderGlobalVulkanMixin.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureAtlasSpriteVulkanMixin.java create mode 100644 src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureMapVulkanMixin.java create mode 100644 src/main/resources/glacier.vulkan.mixins.json create mode 100644 src/main/resources/rustedironcore.vulkan.mixins.json diff --git a/src/main/java/moddedmite/rustedironcore/internal/client/chat/RgbTextColor.java b/src/main/java/moddedmite/rustedironcore/internal/client/chat/RgbTextColor.java new file mode 100644 index 0000000..d6a4478 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/client/chat/RgbTextColor.java @@ -0,0 +1,31 @@ +package moddedmite.rustedironcore.internal.client.chat; + +import net.minecraft.Minecraft; + +/** Colour of the {@code §x} RGB code currently active in {@code FontRenderer.renderStringAtPos}. */ +public interface RgbTextColor { + /** The active RGB colour, -1 when a legacy colour (or none) is active. */ + int ric$activeRgbColor(); + + /** + * {@code rgb} as the RGBA colour {@code renderStringAtPos} sets for a legacy colour code: anaglyph-adjusted and, for the shadow + * pass, darkened the way MITE darkens its own codes. + */ + static float[] toTextColor(int rgb, boolean shadow, float alpha) { + int r = rgb >> 16 & 255; + int g = rgb >> 8 & 255; + int b = rgb & 255; + if (Minecraft.theMinecraft.gameSettings.anaglyph) { + int luminance = (r * 30 + g * 59 + b * 11) / 100; + g = (r * 30 + g * 70) / 100; + b = (r * 30 + b * 70) / 100; + r = luminance; + } + if (shadow) { + r /= 4; + g /= 4; + b /= 4; + } + return new float[]{r / 255.0F, g / 255.0F, b / 255.0F, alpha}; + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/client/texture/InterpolatedSprite.java b/src/main/java/moddedmite/rustedironcore/internal/client/texture/InterpolatedSprite.java new file mode 100644 index 0000000..d4de845 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/client/texture/InterpolatedSprite.java @@ -0,0 +1,9 @@ +package moddedmite.rustedironcore.internal.client.texture; + +import net.minecraft.AnimationMetadataSection; + +/** A {@code TextureAtlasSprite} that blends animation frames when its metadata asks for {@code interpolate}. */ +public interface InterpolatedSprite { + /** Called after the sprite was loaded with {@code sourceMetadata} (the "animation" section of its resource, or null). */ + void rustedIronCore$retainInterpolation(AnimationMetadataSection sourceMetadata); +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/client/texture/LegacyAliasResourceManager.java b/src/main/java/moddedmite/rustedironcore/internal/client/texture/LegacyAliasResourceManager.java new file mode 100644 index 0000000..d5a3e47 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/client/texture/LegacyAliasResourceManager.java @@ -0,0 +1,44 @@ +package moddedmite.rustedironcore.internal.client.texture; + +import moddedmite.rustedironcore.api.resource.LegacyTextureResources; +import net.minecraft.Resource; +import net.minecraft.ResourceLocation; +import net.minecraft.ResourceManager; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** A resource manager whose {@link #getResource} resolves legacy atlas names like {@link LegacyTextureResources#getResource}. */ +public final class LegacyAliasResourceManager implements ResourceManager { + private final ResourceManager delegate; + + public LegacyAliasResourceManager(ResourceManager delegate) { + this.delegate = delegate; + } + + @Override + public Set getResourceDomains() { + return this.delegate.getResourceDomains(); + } + + @Override + public Resource getResource(ResourceLocation location) { + try { + return LegacyTextureResources.getResource(this.delegate, location); + } catch (IOException e) { + throw LegacyAliasResourceManager.sneakyThrow(e); + } + } + + @Override + public List getAllResources(ResourceLocation location) { + return this.delegate.getAllResources(location); + } + + /** MITE's {@code getResource} throws {@link IOException} without declaring it; callers catch it, so it is passed on unchanged. */ + @SuppressWarnings("unchecked") + private static T sneakyThrow(Throwable t) throws T { + throw (T) t; + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/compat/FmlRuntime.java b/src/main/java/moddedmite/rustedironcore/internal/compat/FmlRuntime.java new file mode 100644 index 0000000..453959e --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/compat/FmlRuntime.java @@ -0,0 +1,96 @@ +package moddedmite.rustedironcore.internal.compat; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Detects which FishModLoader runs RIC. + *

+ * FML 3.x renders through OpenGL, so RIC's render mixins target the original MITE method bodies (the legacy route). + * FML 4.x with the Vulkan backend rewrites most render methods for its native renderer, one switchable "area" at a time + * ({@code fml.vulkan.native}, {@code fml.vulkan.native.off}); RIC mixins on such methods take the native route instead. + * An area whose native renderer is switched off runs MITE's code again and therefore uses the legacy route. + */ +public final class FmlRuntime { + public enum Route { + LEGACY, NATIVE + } + + private static final Logger LOGGER = LogManager.getLogger("RustedIronCore"); + private static final String FML_CLASS = "net.xiaoyu233.fml.FishModLoader"; + private static final String VULKAN_BACKEND_CLASS = "net.xiaoyu233.fml.vulkan.VulkanBackend"; + private static final String VULKAN_CONFIG_CLASS = "net.xiaoyu233.fml.vulkan.VulkanConfig"; + + private static final String VERSION = readVersion(); + private static final boolean VULKAN = majorVersion(VERSION) >= 4 && classPresent(VULKAN_BACKEND_CLASS); + private static final Map NATIVE_AREAS = new ConcurrentHashMap<>(); + + static { + LOGGER.info("FishModLoader {} detected, {} render route", VERSION, VULKAN ? "Vulkan (per native area)" : "legacy OpenGL"); + } + + private FmlRuntime() { + } + + /** Version of the running FishModLoader, "unknown" if it cannot be read. */ + public static String fmlVersion() { + return VERSION; + } + + /** True on FishModLoader 4.x with the Vulkan backend. */ + public static boolean isVulkan() { + return VULKAN; + } + + /** True when FML's native renderer owns the methods of {@code area} (names of {@code fishmodloader.native..mixins.json}). */ + public static boolean isNativeArea(String area) { + return VULKAN && NATIVE_AREAS.computeIfAbsent(area, FmlRuntime::queryNativeArea); + } + + public static Route route(String area) { + return isNativeArea(area) ? Route.NATIVE : Route.LEGACY; + } + + private static boolean queryNativeArea(String area) { + boolean active; + try { + active = (Boolean) Class.forName(VULKAN_CONFIG_CLASS).getMethod("areaActive", String.class).invoke(null, area); + } catch (ReflectiveOperationException | LinkageError | ClassCastException e) { + // the native renderer is on by default in FML 4.x + LOGGER.warn("Cannot query FML native area '{}', assuming it is active: {}", area, e.toString()); + active = true; + } + LOGGER.info("FML native area '{}': {} route", area, active ? "native" : "legacy"); + return active; + } + + private static String readVersion() { + // Reflection on purpose: FishModLoader.VERSION is a compile-time constant and would be inlined as the version RIC was built against + try { + return String.valueOf(Class.forName(FML_CLASS).getField("VERSION").get(null)); + } catch (ReflectiveOperationException | LinkageError e) { + LOGGER.warn("Cannot read the FishModLoader version, using the legacy render route: {}", e.toString()); + return "unknown"; + } + } + + static int majorVersion(String version) { + int end = 0; + while (end < version.length() && Character.isDigit(version.charAt(end))) { + end++; + } + return end == 0 ? -1 : Integer.parseInt(version.substring(0, end)); + } + + private static boolean classPresent(String name) { + try { + Class.forName(name, false, FmlRuntime.class.getClassLoader()); + return true; + } catch (ClassNotFoundException | LinkageError e) { + return false; + } + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/compat/NativeGlShim.java b/src/main/java/moddedmite/rustedironcore/internal/compat/NativeGlShim.java new file mode 100644 index 0000000..806c895 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/compat/NativeGlShim.java @@ -0,0 +1,102 @@ +package moddedmite.rustedironcore.internal.compat; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** + * GL11-shaped state calls into FML 4.x's native renderer ({@code net.xiaoyu233.fml.vulkan.render.state.GlShim}), for RIC code on + * the native route. RIC is built against FML 3.x, so the class is bound at runtime. + * Only called on the native route, render thread only (like the shim itself). + */ +public final class NativeGlShim { + private static final String GL_SHIM_CLASS = "net.xiaoyu233.fml.vulkan.render.state.GlShim"; + + private static final MethodHandle ENABLE; + private static final MethodHandle DISABLE; + private static final MethodHandle BLEND_FUNC; + private static final MethodHandle COLOR4F; + private static final MethodHandle LINE_WIDTH; + private static final MethodHandle DEPTH_MASK; + + static { + MethodHandle enable = null, disable = null, blendFunc = null, color4f = null, lineWidth = null, depthMask = null; + if (FmlRuntime.isVulkan()) { + try { + Class shim = Class.forName(GL_SHIM_CLASS); + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + enable = lookup.findStatic(shim, "enable", MethodType.methodType(void.class, int.class)); + disable = lookup.findStatic(shim, "disable", MethodType.methodType(void.class, int.class)); + blendFunc = lookup.findStatic(shim, "blendFunc", MethodType.methodType(void.class, int.class, int.class)); + color4f = lookup.findStatic(shim, "color4f", MethodType.methodType(void.class, float.class, float.class, float.class, float.class)); + lineWidth = lookup.findStatic(shim, "lineWidth", MethodType.methodType(void.class, float.class)); + depthMask = lookup.findStatic(shim, "depthMask", MethodType.methodType(void.class, boolean.class)); + } catch (ReflectiveOperationException | LinkageError e) { + enable = null; + } + } + ENABLE = enable; + DISABLE = disable; + BLEND_FUNC = blendFunc; + COLOR4F = color4f; + LINE_WIDTH = lineWidth; + DEPTH_MASK = depthMask; + } + + private NativeGlShim() { + } + + public static void enable(int cap) { + try { + ENABLE.invokeExact(cap); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static void disable(int cap) { + try { + DISABLE.invokeExact(cap); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static void blendFunc(int src, int dst) { + try { + BLEND_FUNC.invokeExact(src, dst); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static void color4f(float r, float g, float b, float a) { + try { + COLOR4F.invokeExact(r, g, b, a); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static void lineWidth(float width) { + try { + LINE_WIDTH.invokeExact(width); + } catch (Throwable t) { + throw rethrow(t); + } + } + + public static void depthMask(boolean flag) { + try { + DEPTH_MASK.invokeExact(flag); + } catch (Throwable t) { + throw rethrow(t); + } + } + + private static RuntimeException rethrow(Throwable t) { + if (t instanceof RuntimeException runtime) throw runtime; + if (t instanceof Error error) throw error; + throw new IllegalStateException(t); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/config/GlacierMixinConfig.java b/src/main/java/moddedmite/rustedironcore/internal/config/GlacierMixinConfig.java new file mode 100644 index 0000000..8c6885f --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/config/GlacierMixinConfig.java @@ -0,0 +1,12 @@ +package moddedmite.rustedironcore.internal.config; + +import java.util.List; + +import static moddedmite.rustedironcore.internal.compat.FmlRuntime.Route.LEGACY; + +/** Plugin of {@code glacier.mixins.json}. */ +public class GlacierMixinConfig extends RoutedMixinPlugin { + public GlacierMixinConfig() { + super(List.of(new Routed("client.gui.GuiContainerCreativeMixin", "container", LEGACY))); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/config/GlacierVulkanMixinConfig.java b/src/main/java/moddedmite/rustedironcore/internal/config/GlacierVulkanMixinConfig.java new file mode 100644 index 0000000..c6a17fa --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/config/GlacierVulkanMixinConfig.java @@ -0,0 +1,12 @@ +package moddedmite.rustedironcore.internal.config; + +import java.util.List; + +import static moddedmite.rustedironcore.internal.compat.FmlRuntime.Route.NATIVE; + +/** Plugin of {@code glacier.vulkan.mixins.json}: overwrites that must merge before FML 4.x's native redirects. */ +public class GlacierVulkanMixinConfig extends RoutedMixinPlugin { + public GlacierVulkanMixinConfig() { + super(List.of(new Routed("client.gui.GuiContainerCreativeMixin", "container", NATIVE))); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/config/MixinConfig.java b/src/main/java/moddedmite/rustedironcore/internal/config/MixinConfig.java index 6e926c7..619ed0e 100644 --- a/src/main/java/moddedmite/rustedironcore/internal/config/MixinConfig.java +++ b/src/main/java/moddedmite/rustedironcore/internal/config/MixinConfig.java @@ -1,22 +1,24 @@ package moddedmite.rustedironcore.internal.config; import moddedmite.rustedironcore.api.util.Platform; -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import java.util.List; -import java.util.Set; -public class MixinConfig implements IMixinConfigPlugin { - @Override - public void onLoad(String s) { - - } - - @Override - public String getRefMapperConfig() { - return null; +import static moddedmite.rustedironcore.internal.compat.FmlRuntime.Route.LEGACY; +import static moddedmite.rustedironcore.internal.compat.FmlRuntime.Route.NATIVE; + +public class MixinConfig extends RoutedMixinPlugin { + public MixinConfig() { + super(List.of( + new Routed("client.render.RenderBipedMixin", "special", LEGACY), + new Routed("client.render.RenderPlayerMixin", "special", LEGACY), + new Routed("client.FontRendererColorMixin", "text", LEGACY), + new Routed("client.vulkan.FontRendererVulkanMixin", "text", NATIVE), + new Routed("client.render.RenderGlobalMixin", "sky", LEGACY), + new Routed("client.vulkan.RenderGlobalVulkanMixin", "sky", NATIVE), + new Routed("client.render.TextureMapAtlasMixin", "texture", LEGACY), + new Routed("client.vulkan.TextureMapVulkanMixin", "texture", NATIVE), + new Routed("client.vulkan.TextureAtlasSpriteVulkanMixin", "texture", NATIVE))); } @Override @@ -32,24 +34,4 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { } return true; } - - @Override - public void acceptTargets(Set set, Set set1) { - - } - - @Override - public List getMixins() { - return null; - } - - @Override - public void preApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) { - - } - - @Override - public void postApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) { - - } } diff --git a/src/main/java/moddedmite/rustedironcore/internal/config/RoutedMixinPlugin.java b/src/main/java/moddedmite/rustedironcore/internal/config/RoutedMixinPlugin.java new file mode 100644 index 0000000..b6838f8 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/config/RoutedMixinPlugin.java @@ -0,0 +1,72 @@ +package moddedmite.rustedironcore.internal.config; + +import moddedmite.rustedironcore.internal.compat.FmlRuntime; +import net.xiaoyu233.fml.FishModLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Config plugin that contributes the client mixins of the render route the running FishModLoader needs (see {@link FmlRuntime}). + *

+ * Routed mixins are never listed in the JSON, only here, so exactly one variant is loaded: + *

    + *
  • a mixin whose injection points only exist in MITE's original method body has a legacy variant and a native variant;
  • + *
  • a mixin that {@code @Overwrite}s a method FML 4.x's native renderer {@code @Redirect}s is listed by the normal config on the + * legacy route and by a {@code *.vulkan.mixins.json} config on the native route. That config has a lower {@code mixinPriority} than + * FML's native configs (500) so the overwrite merges first and the native redirects can still apply to it.
  • + *
+ */ +public class RoutedMixinPlugin implements IMixinConfigPlugin { + /** {@code mixin} (relative to the config package) is applied when the FML native area {@code area} takes {@code route}. */ + public record Routed(String mixin, String area, FmlRuntime.Route route) { + } + + private final List clientMixins; + + protected RoutedMixinPlugin(List clientMixins) { + this.clientMixins = clientMixins; + } + + @Override + public void onLoad(String mixinPackage) { + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + } + + @Override + public List getMixins() { + if (this.clientMixins.isEmpty() || FishModLoader.isServer()) { + return null; + } + List mixins = new ArrayList<>(); + for (Routed routed : this.clientMixins) { + if (FmlRuntime.route(routed.area()) == routed.route()) mixins.add(routed.mixin()); + } + return mixins; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } +} diff --git a/src/main/java/moddedmite/rustedironcore/internal/config/VulkanMixinConfig.java b/src/main/java/moddedmite/rustedironcore/internal/config/VulkanMixinConfig.java new file mode 100644 index 0000000..b318ac4 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/internal/config/VulkanMixinConfig.java @@ -0,0 +1,14 @@ +package moddedmite.rustedironcore.internal.config; + +import java.util.List; + +import static moddedmite.rustedironcore.internal.compat.FmlRuntime.Route.NATIVE; + +/** Plugin of {@code rustedironcore.vulkan.mixins.json}: overwrites that must merge before FML 4.x's native redirects. */ +public class VulkanMixinConfig extends RoutedMixinPlugin { + public VulkanMixinConfig() { + super(List.of( + new Routed("client.render.RenderBipedMixin", "special", NATIVE), + new Routed("client.render.RenderPlayerMixin", "special", NATIVE))); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/MessageComponentSerializerMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/MessageComponentSerializerMixin.java index 0577449..20bb525 100644 --- a/src/main/java/moddedmite/rustedironcore/mixin/MessageComponentSerializerMixin.java +++ b/src/main/java/moddedmite/rustedironcore/mixin/MessageComponentSerializerMixin.java @@ -101,7 +101,7 @@ private void readClickEvent(JsonElement json, Type type, JsonDeserializationCont } } - @Inject(method = "serializeComponent", at = @At("RETURN")) + @Inject(method = "serializeComponent", at = @At("RETURN"), cancellable = true) private void writeClickEvent(ChatMessageComponent component, Type type, JsonSerializationContext context, CallbackInfoReturnable cir) { if (!cir.getReturnValue().isJsonObject()) { diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererColorMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererColorMixin.java new file mode 100644 index 0000000..c4c8fa8 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererColorMixin.java @@ -0,0 +1,26 @@ +package moddedmite.rustedironcore.mixin.client; + +import moddedmite.rustedironcore.internal.client.chat.RgbTextColor; +import net.minecraft.FontRenderer; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import com.llamalad7.mixinextras.sugar.Local; + +/** Legacy render route: applies the active RGB colour where MITE sets a legacy colour code with {@code glColor4f}. */ +@Mixin(FontRenderer.class) +public abstract class FontRendererColorMixin { + + @Redirect(method = "renderStringAtPos(Ljava/lang/String;Z)V", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 0)) + private void renderRgbColor(float red, float green, float blue, float alpha, @Local(argsOnly = true) boolean shadow) { + int rgb = ((RgbTextColor) this).ric$activeRgbColor(); + if (rgb < 0) { + GL11.glColor4f(red, green, blue, alpha); + return; + } + float[] color = RgbTextColor.toTextColor(rgb, shadow, alpha); + GL11.glColor4f(color[0], color[1], color[2], color[3]); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererMixin.java index ab8a188..2833bb1 100644 --- a/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererMixin.java +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/FontRendererMixin.java @@ -6,20 +6,22 @@ import com.llamalad7.mixinextras.sugar.ref.LocalIntRef; import moddedmite.rustedironcore.internal.chat.LegacyTextFormatting; import moddedmite.rustedironcore.internal.client.chat.LegacyTextWidth; +import moddedmite.rustedironcore.internal.client.chat.RgbTextColor; import net.minecraft.EnumChatFormatting; import net.minecraft.FontRenderer; -import net.minecraft.Minecraft; -import org.lwjgl.opengl.GL11; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; 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.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +/** + * RGB formatting codes, shared by both render routes. The colour itself is applied by {@code FontRendererColorMixin} (OpenGL) or + * {@code FontRendererVulkanMixin} (FML 4.x native renderer), through {@link RgbTextColor}. + */ @Mixin(FontRenderer.class) -public abstract class FontRendererMixin { +public abstract class FontRendererMixin implements RgbTextColor { @Unique private int ric$activeRgbColor = -1; @@ -45,31 +47,6 @@ private EnumChatFormatting decodeRgbFormatting(char code, Operation> 16 & 255; - int g = rgb >> 8 & 255; - int b = rgb & 255; - if (Minecraft.theMinecraft.gameSettings.anaglyph) { - int luminance = (r * 30 + g * 59 + b * 11) / 100; - g = (r * 30 + g * 70) / 100; - b = (r * 30 + b * 70) / 100; - r = luminance; - } - if (shadow) { - r /= 4; - g /= 4; - b /= 4; - } - GL11.glColor4f(r / 255.0F, g / 255.0F, b / 255.0F, alpha); - } - @Inject(method = "sizeStringToWidth(Ljava/lang/String;I)I", at = @At("HEAD"), cancellable = true) private void measureRgbFormattedText(String text, int maxWidth, CallbackInfoReturnable cir) { if (text.indexOf(LegacyTextFormatting.CODE) < 0) { @@ -95,4 +72,8 @@ private static void preserveRgbForWrappedText(String text, CallbackInfoReturnabl cir.setReturnValue(LegacyTextFormatting.preserveLastRgbColor(text, cir.getReturnValue())); } + @Override + public int ric$activeRgbColor() { + return this.ric$activeRgbColor; + } } diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureAtlasSpriteMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureAtlasSpriteMixin.java index 3c6c0d1..730ba3c 100644 --- a/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureAtlasSpriteMixin.java +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureAtlasSpriteMixin.java @@ -1,6 +1,7 @@ package moddedmite.rustedironcore.mixin.client.render; import moddedmite.rustedironcore.internal.client.texture.InterpolatedAnimationMetadata; +import moddedmite.rustedironcore.internal.client.texture.InterpolatedSprite; import net.minecraft.AnimationMetadataSection; import net.minecraft.Resource; import net.minecraft.TextureAtlasSprite; @@ -15,7 +16,7 @@ import java.util.List; @Mixin(TextureAtlasSprite.class) -public abstract class TextureAtlasSpriteMixin { +public abstract class TextureAtlasSpriteMixin implements InterpolatedSprite { @Shadow protected List framesTextureData; @@ -41,7 +42,11 @@ public abstract class TextureAtlasSpriteMixin { @Inject(method = "loadSprite", at = @At("RETURN")) private void retainInterpolateMetadata(Resource resource, CallbackInfo ci) { - AnimationMetadataSection sourceMetadata = (AnimationMetadataSection) resource.getMetadata("animation"); + this.rustedIronCore$retainInterpolation((AnimationMetadataSection) resource.getMetadata("animation")); + } + + @Override + public void rustedIronCore$retainInterpolation(AnimationMetadataSection sourceMetadata) { this.rustedIronCore$interpolateFrames = sourceMetadata != null && ((InterpolatedAnimationMetadata) sourceMetadata).rustedIronCore$interpolateFrames(); } diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapAtlasMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapAtlasMixin.java new file mode 100644 index 0000000..b418c18 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapAtlasMixin.java @@ -0,0 +1,23 @@ +package moddedmite.rustedironcore.mixin.client.render; + +import moddedmite.rustedironcore.api.resource.LegacyTextureResources; +import net.minecraft.Resource; +import net.minecraft.ResourceLocation; +import net.minecraft.ResourceManager; +import net.minecraft.TextureMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.io.IOException; + +/** Legacy render route: atlas sprites are read in MITE's {@code loadTextureAtlas}. */ +@Mixin(TextureMap.class) +public abstract class TextureMapAtlasMixin { + + @Redirect(method = "loadTextureAtlas", at = @At(value = "INVOKE", target = "Lnet/minecraft/ResourceManager;getResource(Lnet/minecraft/ResourceLocation;)Lnet/minecraft/Resource;")) + private Resource loadModernAtlasTexture(ResourceManager manager, ResourceLocation legacyLocation) + throws IOException { + return LegacyTextureResources.getResource(manager, legacyLocation); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapMixin.java index b107ae7..f60fe11 100644 --- a/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapMixin.java +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/render/TextureMapMixin.java @@ -2,19 +2,12 @@ import moddedmite.rustedironcore.api.event.Handlers; import moddedmite.rustedironcore.api.model.JsonBlockModelManager; -import moddedmite.rustedironcore.api.resource.LegacyTextureResources; -import net.minecraft.Resource; -import net.minecraft.ResourceLocation; -import net.minecraft.ResourceManager; import net.minecraft.TextureMap; 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.CallbackInfo; -import java.io.IOException; - @Mixin(TextureMap.class) public abstract class TextureMapMixin { @@ -24,10 +17,4 @@ private void registerJsonModelTextures(CallbackInfo ci) { Handlers.JsonModel.onJsonModelRegister(); JsonBlockModelManager.INSTANCE.registerTextureAtlasSprites((TextureMap) (Object) this); } - - @Redirect(method = "loadTextureAtlas", at = @At(value = "INVOKE", target = "Lnet/minecraft/ResourceManager;getResource(Lnet/minecraft/ResourceLocation;)Lnet/minecraft/Resource;")) - private Resource loadModernAtlasTexture(ResourceManager manager, ResourceLocation legacyLocation) - throws IOException { - return LegacyTextureResources.getResource(manager, legacyLocation); - } } diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/FontRendererVulkanMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/FontRendererVulkanMixin.java new file mode 100644 index 0000000..5136525 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/FontRendererVulkanMixin.java @@ -0,0 +1,28 @@ +package moddedmite.rustedironcore.mixin.client.vulkan; + +import com.llamalad7.mixinextras.sugar.Local; +import moddedmite.rustedironcore.internal.client.chat.RgbTextColor; +import moddedmite.rustedironcore.internal.compat.NativeGlShim; +import net.minecraft.FontRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * FML 4.x native route of {@code FontRendererColorMixin}: FML's native {@code renderStringAtPos} sets the colour of a legacy colour + * code with {@code Draw.color(FFFF)} instead of {@code GL11.glColor4f}. + */ +@Mixin(FontRenderer.class) +public abstract class FontRendererVulkanMixin { + + @Redirect(method = "renderStringAtPos(Ljava/lang/String;Z)V", at = @At(value = "INVOKE", target = "Lnet/xiaoyu233/fml/vulkan/render/state/Draw;color(FFFF)V", ordinal = 0, remap = false)) + private void renderRgbColor(float red, float green, float blue, float alpha, @Local(argsOnly = true) boolean shadow) { + int rgb = ((RgbTextColor) this).ric$activeRgbColor(); + if (rgb < 0) { + NativeGlShim.color4f(red, green, blue, alpha); + return; + } + float[] color = RgbTextColor.toTextColor(rgb, shadow, alpha); + NativeGlShim.color4f(color[0], color[1], color[2], color[3]); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/RenderGlobalVulkanMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/RenderGlobalVulkanMixin.java new file mode 100644 index 0000000..36fa34b --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/RenderGlobalVulkanMixin.java @@ -0,0 +1,56 @@ +package moddedmite.rustedironcore.mixin.client.vulkan; + +import moddedmite.rustedironcore.internal.compat.NativeGlShim; +import net.minecraft.AxisAlignedBB; +import net.minecraft.Block; +import net.minecraft.EntityPlayer; +import net.minecraft.RaycastCollision; +import net.minecraft.RenderGlobal; +import net.minecraft.WorldClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * FML 4.x native route of {@code RenderGlobalMixin}: FML's native {@code drawSelectionBox} no longer calls + * {@code drawOutlinedBoundingBox}, so a block with several collision boxes is outlined here, with MITE's selection box state. + */ +@Mixin(RenderGlobal.class) +public abstract class RenderGlobalVulkanMixin { + + @Shadow + private WorldClient theWorld; + + @Invoker("drawOutlinedBoundingBox") + protected abstract void rusted_iron_core$drawOutlinedBoundingBox(AxisAlignedBB bounds); + + @Inject(method = "drawSelectionBox", at = @At("HEAD"), cancellable = true) + private void drawSelectionBounds(EntityPlayer player, RaycastCollision rc, int pass, float partialTicks, CallbackInfo ci) { + if (pass != 0 || !rc.isBlock()) return; + Block block = rc.getBlockHit(); + Object collisionBounds = block.getCollisionBounds(this.theWorld, rc.block_hit_x, rc.block_hit_y, rc.block_hit_z, null); + if (!(collisionBounds instanceof AxisAlignedBB[] bounds)) return; + + NativeGlShim.enable(3042); + NativeGlShim.blendFunc(770, 771); + NativeGlShim.color4f(0.0F, 0.0F, 0.0F, 0.4F); + NativeGlShim.lineWidth(2.0F); + NativeGlShim.disable(3553); + NativeGlShim.depthMask(false); + double cameraX = player.lastTickPosX + (player.posX - player.lastTickPosX) * partialTicks; + double cameraY = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialTicks; + double cameraZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks; + double epsilon = 0.002D; + for (AxisAlignedBB bound : bounds) { + if (bound == null) continue; + this.rusted_iron_core$drawOutlinedBoundingBox(bound.expand(epsilon, epsilon, epsilon).getOffsetBoundingBox(rc.block_hit_x - cameraX, rc.block_hit_y - cameraY, rc.block_hit_z - cameraZ)); + } + NativeGlShim.depthMask(true); + NativeGlShim.enable(3553); + NativeGlShim.disable(3042); + ci.cancel(); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureAtlasSpriteVulkanMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureAtlasSpriteVulkanMixin.java new file mode 100644 index 0000000..9ff333c --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureAtlasSpriteVulkanMixin.java @@ -0,0 +1,22 @@ +package moddedmite.rustedironcore.mixin.client.vulkan; + +import moddedmite.rustedironcore.internal.client.texture.InterpolatedSprite; +import net.minecraft.AnimationMetadataSection; +import net.minecraft.TextureAtlasSprite; +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; + +/** + * FML 4.x native route of {@code TextureAtlasSpriteMixin.retainInterpolateMetadata}: atlas sprites are decoded on FML's worker pool + * and loaded with {@code fml$loadDecoded} (added by FML's native texture mixin) instead of {@code loadSprite}. + */ +@Mixin(TextureAtlasSprite.class) +public abstract class TextureAtlasSpriteVulkanMixin { + + @Inject(method = "fml$loadDecoded(II[ILnet/minecraft/AnimationMetadataSection;)V", at = @At("RETURN"), remap = false) + private void retainInterpolateMetadata(int imageWidth, int imageHeight, int[] pixels, AnimationMetadataSection animation, CallbackInfo ci) { + ((InterpolatedSprite) this).rustedIronCore$retainInterpolation(animation); + } +} diff --git a/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureMapVulkanMixin.java b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureMapVulkanMixin.java new file mode 100644 index 0000000..ec9f549 --- /dev/null +++ b/src/main/java/moddedmite/rustedironcore/mixin/client/vulkan/TextureMapVulkanMixin.java @@ -0,0 +1,21 @@ +package moddedmite.rustedironcore.mixin.client.vulkan; + +import moddedmite.rustedironcore.internal.client.texture.LegacyAliasResourceManager; +import net.minecraft.ResourceManager; +import net.minecraft.TextureMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +/** + * FML 4.x native route of {@code TextureMapAtlasMixin}: FML's native {@code loadTextureAtlas} reads the sprites through + * {@code AtlasPrefetch.fetch(int, ResourceManager, ResourceLocation)}, so the legacy name aliases are applied to the manager it gets. + */ +@Mixin(TextureMap.class) +public abstract class TextureMapVulkanMixin { + + @ModifyArg(method = "loadTextureAtlas", at = @At(value = "INVOKE", target = "Lnet/xiaoyu233/fml/vulkan/render/texture/AtlasPrefetch;fetch(ILnet/minecraft/ResourceManager;Lnet/minecraft/ResourceLocation;)V", remap = false), index = 1) + private ResourceManager loadModernAtlasTextures(ResourceManager manager) { + return new LegacyAliasResourceManager(manager); + } +} diff --git a/src/main/resources/fml.mod.json b/src/main/resources/fml.mod.json index 8a239ed..b378cf5 100644 --- a/src/main/resources/fml.mod.json +++ b/src/main/resources/fml.mod.json @@ -32,7 +32,9 @@ }, "mixins": [ "rustedironcore.mixins.json", - "glacier.mixins.json" + "rustedironcore.vulkan.mixins.json", + "glacier.mixins.json", + "glacier.vulkan.mixins.json" ], "accessWidener": "rustedironcore.accesswidener", "custom": { diff --git a/src/main/resources/glacier.mixins.json b/src/main/resources/glacier.mixins.json index d74d4ab..be56a66 100644 --- a/src/main/resources/glacier.mixins.json +++ b/src/main/resources/glacier.mixins.json @@ -6,13 +6,13 @@ "minVersion": "0.8.3", "mixins": [ "MinecraftServerMixin", - "client.gui.GuiContainerCreativeMixin", "creativetab.CreativeTabsMixin", "util.ReferenceFileWriterMixin" ], "client": [ "client.MinecraftClientMixin" ], + "plugin": "moddedmite.rustedironcore.internal.config.GlacierMixinConfig", "overwrites": { "requireAnnotations": true } diff --git a/src/main/resources/glacier.vulkan.mixins.json b/src/main/resources/glacier.vulkan.mixins.json new file mode 100644 index 0000000..6cac25f --- /dev/null +++ b/src/main/resources/glacier.vulkan.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "huix.glacier.mixin", + "compatibilityLevel": "JAVA_17", + "target": "DEFAULT", + "minVersion": "0.8.3", + "mixinPriority": 400, + "overwrites": { + "requireAnnotations": true + }, + "plugin": "moddedmite.rustedironcore.internal.config.GlacierVulkanMixinConfig" +} diff --git a/src/main/resources/rustedironcore.mixins.json b/src/main/resources/rustedironcore.mixins.json index 757a56c..9ad1240 100644 --- a/src/main/resources/rustedironcore.mixins.json +++ b/src/main/resources/rustedironcore.mixins.json @@ -40,12 +40,9 @@ "client.render.ItemRendererMixin", "client.render.LayeredTextureMixin", "client.render.RenderArrowMixin", - "client.render.RenderBipedMixin", "client.render.RenderBlocksMixin", - "client.render.RenderGlobalMixin", "client.render.RenderItemMixin", "client.render.RenderMixin", - "client.render.RenderPlayerMixin", "client.render.RenderVillagerMixin", "client.render.SimpleTextureMixin", "client.render.TextureAtlasSpriteMixin", diff --git a/src/main/resources/rustedironcore.vulkan.mixins.json b/src/main/resources/rustedironcore.vulkan.mixins.json new file mode 100644 index 0000000..44c0a0a --- /dev/null +++ b/src/main/resources/rustedironcore.vulkan.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "package": "moddedmite.rustedironcore.mixin", + "compatibilityLevel": "JAVA_17", + "target": "DEFAULT", + "minVersion": "0.8.3", + "mixinPriority": 400, + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + }, + "plugin": "moddedmite.rustedironcore.internal.config.VulkanMixinConfig" +}