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: + *

+ */ +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" +}