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. 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..3e00fb5 --- /dev/null +++ b/src/main/java/dev/httxrafa/modflared/mixin/client/ServerListEntryNormalMixin.java @@ -0,0 +1,87 @@ +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.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; +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_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) { + 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; + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + 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$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 ec88a47..34bb0ee 100644 --- a/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java +++ b/src/main/java/dev/httxrafa/modflared/tunnel/TunnelStatus.java @@ -1,8 +1,9 @@ package dev.httxrafa.modflared.tunnel; +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 net.minecraft.util.text.ITextComponent; import java.util.ArrayList; import java.util.Collections; @@ -29,15 +30,23 @@ 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(translate("gui.tunnel.status.use", "Using Cloudflare tunnel", 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(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, 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": [],