From 6d7a2d6cb32399c2faca07e0eb1b8b4f39efaf86 Mon Sep 17 00:00:00 2001 From: Hatta Date: Tue, 5 May 2026 13:38:04 +0800 Subject: [PATCH 1/4] feat: Show modflared indicator in server list Add a client mixin for multiplayer server-list entries so servers routed through modflared display the existing indicator icon. Position the indicator away from the Forge modded-server compatibility badge and use direct visible strings for the hover and connection status text to avoid untranslated lang keys appearing in-game. Register the new mixin and keep the language entry for the server-list tooltip resource. --- .../client/ServerListEntryNormalMixin.java | 72 +++++++++++++++++++ .../modflared/tunnel/TunnelStatus.java | 12 ++-- .../assets/modflared/lang/en_us.lang | 1 + src/main/resources/modflared.mixins.json | 1 + 4 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java diff --git a/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java new file mode 100644 index 0000000..cbd341d --- /dev/null +++ b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java @@ -0,0 +1,72 @@ +package dev.httxrafa.modflared.mixin.client; + +import dev.httxrafa.modflared.Modflared; +import dev.httxrafa.modflared.interfaces.mixin.IServerData; +import dev.httxrafa.modflared.tunnel.TunnelStatus; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiMultiplayer; +import net.minecraft.client.gui.ServerListEntryNormal; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.callback.CallbackInfo; + +@Mixin(ServerListEntryNormal.class) +public abstract class ServerListEntryNormalMixin { + + @Shadow + @Final + private GuiMultiplayer owner; + + @Shadow + @Final + private ServerData server; + + @Unique + private static final ResourceLocation MODFLARED_INDICATOR_TEXTURE = new ResourceLocation( + Modflared.MOD_ID, + "textures/gui/sprites/icon/indicator.png" + ); + + @Unique + private static final int MODFLARED_INDICATOR_SIZE = 10; + + @Unique + private static final int MODFLARED_INDICATOR_RIGHT_OFFSET = 28; + + @Unique + private static final String MODFLARED_INDICATOR_TOOLTIP = "Modflared in use"; + + @Inject(method = "drawEntry", at = @At("TAIL")) + private void modflared$drawTunnelIndicator(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected, float partialTicks, CallbackInfo callbackInfo) { + TunnelStatus tunnelStatus = ((IServerData) this.server).getTunnelStatus(); + if (tunnelStatus == null || tunnelStatus.getState() != TunnelStatus.State.USE) { + return; + } + + int indicatorX = x + listWidth - MODFLARED_INDICATOR_RIGHT_OFFSET; + int indicatorY = y + 11; + + Minecraft.getMinecraft().getTextureManager().bindTexture(MODFLARED_INDICATOR_TEXTURE); + Gui.drawModalRectWithCustomSizedTexture( + indicatorX, + indicatorY, + 0.0F, + 0.0F, + MODFLARED_INDICATOR_SIZE, + MODFLARED_INDICATOR_SIZE, + MODFLARED_INDICATOR_SIZE, + MODFLARED_INDICATOR_SIZE + ); + + if (mouseX >= indicatorX && mouseX <= indicatorX + MODFLARED_INDICATOR_SIZE && mouseY >= indicatorY && mouseY <= indicatorY + MODFLARED_INDICATOR_SIZE) { + this.owner.setHoveringText(MODFLARED_INDICATOR_TOOLTIP); + } + } +} diff --git a/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java b/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java index ec88a47..b8ad07d 100644 --- a/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java +++ b/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java @@ -1,8 +1,8 @@ package dev.httxrafa.modflared.tunnel; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; import java.util.ArrayList; import java.util.Collections; @@ -29,11 +29,11 @@ public State getState() { public List generateFeedback() { List feedback = new ArrayList(); if (state == State.USE) { - feedback.add(new TextComponentTranslation("gui.tunnel.status.use").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.AQUA))); + feedback.add(new TextComponentString("Using Cloudflare tunnel").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.AQUA))); } else if (state == State.FAILED_TO_DETERMINE) { - feedback.add(new TextComponentTranslation("gui.tunnel.status.failed.0").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); - feedback.add(new TextComponentTranslation("gui.tunnel.status.failed.1").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); - feedback.add(new TextComponentTranslation("gui.tunnel.status.failed.2").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); + feedback.add(new TextComponentString("Modflared could not determine if a tunnel is required.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); + feedback.add(new TextComponentString("The connection will continue without a tunnel.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); + feedback.add(new TextComponentString("Add this server to forced_tunnels.json if it must use a tunnel.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); } return Collections.unmodifiableList(feedback); } diff --git a/src/main/resources/assets/modflared/lang/en_us.lang b/src/main/resources/assets/modflared/lang/en_us.lang index e079fca..f386631 100644 --- a/src/main/resources/assets/modflared/lang/en_us.lang +++ b/src/main/resources/assets/modflared/lang/en_us.lang @@ -1,4 +1,5 @@ gui.tunnel.status.use=Using Cloudflare tunnel +gui.multiplayer.tunnel.status.0=Modflared in use gui.tunnel.status.failed.0=Modflared could not determine if a tunnel is required. gui.tunnel.status.failed.1=The connection will continue without a tunnel. gui.tunnel.status.failed.2=Add this server to forced_tunnels.json if it must use a tunnel. diff --git a/src/main/resources/modflared.mixins.json b/src/main/resources/modflared.mixins.json index 503ebd2..84d023a 100644 --- a/src/main/resources/modflared.mixins.json +++ b/src/main/resources/modflared.mixins.json @@ -9,6 +9,7 @@ "client.GuiConnectingMixin", "client.GuiConnectingThreadMixin", "client.ServerDataMixin", + "client.ServerListEntryNormalMixin", "client.ServerPingerMixin" ], "client": [], From e2f810715acc99616d3966aa0e3b7c30866d7ea4 Mon Sep 17 00:00:00 2001 From: Hatta Date: Tue, 5 May 2026 13:48:53 +0800 Subject: [PATCH 2/4] fix: Reset GL color before drawing indicator Reset the render color to white before binding and drawing the modflared server-list indicator. This prevents the indicator texture from inheriting tint from previous server-list rendering state or other modded indicators. --- .../modflared/mixin/client/ServerListEntryNormalMixin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java index cbd341d..93baa1a 100644 --- a/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java +++ b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java @@ -8,6 +8,7 @@ import net.minecraft.client.gui.GuiMultiplayer; import net.minecraft.client.gui.ServerListEntryNormal; import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -53,6 +54,7 @@ public abstract class ServerListEntryNormalMixin { int indicatorX = x + listWidth - MODFLARED_INDICATOR_RIGHT_OFFSET; int indicatorY = y + 11; + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); Minecraft.getMinecraft().getTextureManager().bindTexture(MODFLARED_INDICATOR_TEXTURE); Gui.drawModalRectWithCustomSizedTexture( indicatorX, From 56f17741cf1facf6bc1c857cb919dd002647d1bd Mon Sep 17 00:00:00 2001 From: Hatta Date: Tue, 5 May 2026 13:58:09 +0800 Subject: [PATCH 3/4] fix: Use localized tunnel text with fallbacks Restore localization lookups for the server-list tooltip and connection feedback while preserving English fallbacks when the legacy client returns unresolved translation keys. This addresses the review feedback without reintroducing the in-game issue where raw translation keys appeared in the UI. --- .../client/ServerListEntryNormalMixin.java | 17 +++++++++++++++-- .../httxrafa/modflared/tunnel/TunnelStatus.java | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java index 93baa1a..3e00fb5 100644 --- a/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java +++ b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java @@ -9,6 +9,7 @@ import net.minecraft.client.gui.ServerListEntryNormal; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; import net.minecraft.util.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -42,7 +43,10 @@ public abstract class ServerListEntryNormalMixin { private static final int MODFLARED_INDICATOR_RIGHT_OFFSET = 28; @Unique - private static final String MODFLARED_INDICATOR_TOOLTIP = "Modflared in use"; + private static final String MODFLARED_INDICATOR_TOOLTIP_KEY = "gui.multiplayer.tunnel.status.0"; + + @Unique + private static final String MODFLARED_INDICATOR_TOOLTIP_FALLBACK = "Modflared in use"; @Inject(method = "drawEntry", at = @At("TAIL")) private void modflared$drawTunnelIndicator(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected, float partialTicks, CallbackInfo callbackInfo) { @@ -68,7 +72,16 @@ public abstract class ServerListEntryNormalMixin { ); if (mouseX >= indicatorX && mouseX <= indicatorX + MODFLARED_INDICATOR_SIZE && mouseY >= indicatorY && mouseY <= indicatorY + MODFLARED_INDICATOR_SIZE) { - this.owner.setHoveringText(MODFLARED_INDICATOR_TOOLTIP); + this.owner.setHoveringText(modflared$translate(MODFLARED_INDICATOR_TOOLTIP_KEY, MODFLARED_INDICATOR_TOOLTIP_FALLBACK)); + } + } + + @Unique + private static String modflared$translate(String key, String fallback) { + String translated = I18n.format(key); + if (translated == null || translated.equals(key)) { + return fallback; } + return translated; } } diff --git a/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java b/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java index b8ad07d..34bb0ee 100644 --- a/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java +++ b/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java @@ -2,6 +2,7 @@ import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextFormatting; import java.util.ArrayList; @@ -29,15 +30,23 @@ public State getState() { public List generateFeedback() { List feedback = new ArrayList(); if (state == State.USE) { - feedback.add(new TextComponentString("Using Cloudflare tunnel").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.AQUA))); + feedback.add(translate("gui.tunnel.status.use", "Using Cloudflare tunnel", TextFormatting.AQUA)); } else if (state == State.FAILED_TO_DETERMINE) { - feedback.add(new TextComponentString("Modflared could not determine if a tunnel is required.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); - feedback.add(new TextComponentString("The connection will continue without a tunnel.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); - feedback.add(new TextComponentString("Add this server to forced_tunnels.json if it must use a tunnel.").setStyle(new net.minecraft.util.text.Style().setColor(TextFormatting.RED))); + feedback.add(translate("gui.tunnel.status.failed.0", "Modflared could not determine if a tunnel is required.", TextFormatting.RED)); + feedback.add(translate("gui.tunnel.status.failed.1", "The connection will continue without a tunnel.", TextFormatting.RED)); + feedback.add(translate("gui.tunnel.status.failed.2", "Add this server to forced_tunnels.json if it must use a tunnel.", TextFormatting.RED)); } return Collections.unmodifiableList(feedback); } + private static ITextComponent translate(String key, String fallback, TextFormatting formatting) { + ITextComponent component = new TextComponentTranslation(key); + if (component.getUnformattedText().equals(key)) { + component = new TextComponentString(fallback); + } + return component.setStyle(new net.minecraft.util.text.Style().setColor(formatting)); + } + public enum State { USE, DONT_USE, From e2bbe4bbc5311350a8cfbc82c8891ec97c2c26c1 Mon Sep 17 00:00:00 2001 From: Hatta Date: Tue, 5 May 2026 14:30:38 +0800 Subject: [PATCH 4/4] chore: Remove legacy label from Forge version Update the Forge 1.12.2 artifact version from 1.12.2-legacy.1 to 1.12.2-1 now that the branch is tested and feature complete. This changes the generated jar names to drop the legacy qualifier while preserving the mod id and build configuration. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d6efc3a..ab4942a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ mcp_mappings=stable_39 mod_id=modflared mod_name=Modflared mod_license=MIT -mod_version=1.12.2-legacy.1 +mod_version=1.12.2-1 mod_group_id=dev.httxrafa.modflared mod_authors=HttpRafa, Contributors mod_description=Automatically connects you to a Cloudflare tunnel without having to install cloudflared separately.