diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/ViaForge114.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/ViaForge114.java index 5a8be95..78b7bf1 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/ViaForge114.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/ViaForge114.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.client.Minecraft; +import net.minecraft.util.Session; import net.minecraft.util.SharedConstants; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -43,4 +46,16 @@ public class ViaForge114 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final Session session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 87b4d10..0450d80 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -17,6 +17,9 @@ */ package de.florianmichael.viaforge.gui; +import com.mojang.realmsclient.gui.ChatFormatting; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; @@ -24,53 +27,98 @@ import net.minecraft.client.gui.widget.button.Button; import net.minecraft.client.gui.widget.list.AbstractList; import net.minecraft.client.gui.widget.list.ExtendedList; import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TextFormatting; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(new StringTextComponent("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addButton(new Button(5, height - 25, 20, 20, "<-", b -> minecraft.setScreen(parent))); + if (!this.simple) { + addButton(new Button(width - 105, 5, 100, 20, "Create dump", b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(ChatFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(ChatFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + })); + addButton(new Button(width - 105, height - 25, 100, 20, "Reload configs", b -> Via.getManager().getConfigurationProvider().reloadConfigs())); + } - slotList = new SlotList(minecraft, width, height, 32, height - 32, 10); - addButton(new Button(width / 2 - 100, height - 27, 200, 20, "Back", b -> minecraft.setScreen(parent))); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); } @Override public void render(int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + renderBackground(); - this.slotList.render(p_230430_2_, p_230430_3_, p_230430_4_); + this.list.render(p_230430_2_, p_230430_3_, p_230430_4_); super.render(p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(this.font, TextFormatting.GOLD + "ViaForge", this.width / 4, 6, 16777215); + drawCenteredString(font, ChatFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ExtendedList { + class SlotList extends ExtendedList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); @@ -86,16 +134,29 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override - public void render(int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? TextFormatting.GREEN.toString() : TextFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public void render(int p_230432_2_, int y, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? ChatFormatting.GOLD.toString() : ChatFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? ChatFormatting.WHITE.toString() : ChatFormatting.DARK_RED.toString(); + } + + drawCenteredString(Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java new file mode 100644 index 0000000..3d934d6 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java @@ -0,0 +1,64 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.screen.AddServerScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.text.ITextComponent; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AddServerScreen.class) +public class MixinAddServerScreen extends Screen { + + @Shadow @Final private ServerData serverData; + + public MixinAddServerScreen(ITextComponent title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addButton(new Button(pos.key(), pos.value(), 100, 20, target != null ? target.getName() : "Set Version", b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + })); + } + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java new file mode 100644 index 0000000..4e762a5 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.network.login.ClientLoginNetHandler; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientLoginNetHandler.class) +public class MixinClientLoginNetHandler { + + @Shadow @Final private NetworkManager connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java new file mode 100644 index 0000000..c0b3129 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetAddress; + +@Mixin(targets = "net.minecraft.client.gui.screen.ConnectingScreen$1") +public class MixinConnectingScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java new file mode 100644 index 0000000..add58e7 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.overlay.DebugOverlayGui; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugOverlayGui.class) +public class MixinDebugOverlayGui { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java index 794bd0d..5f510aa 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge114; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MainMenuScreen; import net.minecraft.client.gui.screen.Screen; @@ -32,16 +34,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MainMenuScreen.class) public class MixinMainMenuScreen extends Screen { - public MixinMainMenuScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMainMenuScreen(ITextComponent title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge114.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addButton(new Button(5, 6, 98, 20,"ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java index e697dca..71a4e7f 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge114; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MultiplayerScreen; import net.minecraft.client.gui.screen.Screen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MultiplayerScreen.class) public class MixinMultiplayerScreen extends Screen { - protected MixinMultiplayerScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMultiplayerScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addButton(new Button(5, 6, 98, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java index a9dd995..796e12a 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java @@ -18,22 +18,89 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NettyEncryptingDecoder; +import net.minecraft.network.NettyEncryptingEncoder; import net.minecraft.network.NetworkManager; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.util.CryptManager; +import net.minecraft.util.LazyLoadBase; +import net.minecraft.util.text.ITextComponent; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import java.net.InetAddress; @Mixin(NetworkManager.class) -public class MixinNetworkManager { +public class MixinNetworkManager implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetAddress address, int serverPort, boolean useNativeTransport, CallbackInfoReturnable cir, NetworkManager networkmanager, Class oclass, LazyLoadBase lazyloadbase) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) networkmanager).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(SecretKey key, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = CryptManager.getCipher(2, key); + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new NettyEncryptingEncoder(CryptManager.getCipher(1, key))); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(ITextComponent p_150718_1_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_setCompressionTreshold_1_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new NettyEncryptingDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java index 86826ed..0bca0b1 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NetworkManager; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.NetworkManager$1", remap = false) +@Mixin(targets = "net.minecraft.network.NetworkManager$1") public class MixinNetworkManager_1 { + @Final + @Mutable + NetworkManager val$networkmanager; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$networkmanager); } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..e62390e --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundNBT; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundNBT;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundNBT compoundnbt) { + if (viaforge_version != null) { + compoundnbt.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundNBT compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java index 63bfca1..e26166a 100644 --- a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java @@ -17,7 +17,9 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ServerListScreen; @@ -31,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ServerListScreen.class) public class MixinServerListScreen extends Screen { - protected MixinServerListScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinServerListScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addButton(new Button(5, 6, 98, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java new file mode 100644 index 0000000..b92df76 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.network.ServerPinger; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetAddress; + +@Mixin(ServerPinger.class) +public class MixinServerPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc114/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc114/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc114/src/main/resources/mixins.viaforge-mc114.json b/viaforge-mc114/src/main/resources/mixins.viaforge-mc114.json index 34c326f..064a646 100644 --- a/viaforge-mc114/src/main/resources/mixins.viaforge-mc114.json +++ b/viaforge-mc114/src/main/resources/mixins.viaforge-mc114.json @@ -4,15 +4,24 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinAddServerScreen", + "MixinClientLoginNetHandler", + "MixinConnectingScreen_1", + "MixinDebugOverlayGui", "MixinMainMenuScreen", "MixinMultiplayerScreen", "MixinNetworkManager", "MixinNetworkManager_1", + "MixinServerData", "MixinServerListScreen", + "MixinServerPinger", "fixes.MixinClientPlayerEntity" ], "injectors": { "defaultRequire": 1 }, - "refmap": "mixins.viaforge-mc114.refmap.json" + "refmap": "mixins.viaforge-mc114.refmap.json", + "mixins": [ + "MixinNetworkManager_1" + ] } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/ViaForge115.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/ViaForge115.java index 5e047ae..535f06d 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/ViaForge115.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/ViaForge115.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.client.Minecraft; +import net.minecraft.util.Session; import net.minecraft.util.SharedConstants; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge115 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final Session session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 87b4d10..0450d80 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -17,6 +17,9 @@ */ package de.florianmichael.viaforge.gui; +import com.mojang.realmsclient.gui.ChatFormatting; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; @@ -24,53 +27,98 @@ import net.minecraft.client.gui.widget.button.Button; import net.minecraft.client.gui.widget.list.AbstractList; import net.minecraft.client.gui.widget.list.ExtendedList; import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TextFormatting; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(new StringTextComponent("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addButton(new Button(5, height - 25, 20, 20, "<-", b -> minecraft.setScreen(parent))); + if (!this.simple) { + addButton(new Button(width - 105, 5, 100, 20, "Create dump", b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(ChatFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(ChatFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + })); + addButton(new Button(width - 105, height - 25, 100, 20, "Reload configs", b -> Via.getManager().getConfigurationProvider().reloadConfigs())); + } - slotList = new SlotList(minecraft, width, height, 32, height - 32, 10); - addButton(new Button(width / 2 - 100, height - 27, 200, 20, "Back", b -> minecraft.setScreen(parent))); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); } @Override public void render(int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + renderBackground(); - this.slotList.render(p_230430_2_, p_230430_3_, p_230430_4_); + this.list.render(p_230430_2_, p_230430_3_, p_230430_4_); super.render(p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(this.font, TextFormatting.GOLD + "ViaForge", this.width / 4, 6, 16777215); + drawCenteredString(font, ChatFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ExtendedList { + class SlotList extends ExtendedList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); @@ -86,16 +134,29 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override - public void render(int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? TextFormatting.GREEN.toString() : TextFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public void render(int p_230432_2_, int y, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? ChatFormatting.GOLD.toString() : ChatFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? ChatFormatting.WHITE.toString() : ChatFormatting.DARK_RED.toString(); + } + + drawCenteredString(Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java new file mode 100644 index 0000000..3d934d6 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java @@ -0,0 +1,64 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.screen.AddServerScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.text.ITextComponent; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AddServerScreen.class) +public class MixinAddServerScreen extends Screen { + + @Shadow @Final private ServerData serverData; + + public MixinAddServerScreen(ITextComponent title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addButton(new Button(pos.key(), pos.value(), 100, 20, target != null ? target.getName() : "Set Version", b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + })); + } + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java new file mode 100644 index 0000000..4e762a5 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.network.login.ClientLoginNetHandler; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientLoginNetHandler.class) +public class MixinClientLoginNetHandler { + + @Shadow @Final private NetworkManager connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java new file mode 100644 index 0000000..c0b3129 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetAddress; + +@Mixin(targets = "net.minecraft.client.gui.screen.ConnectingScreen$1") +public class MixinConnectingScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java new file mode 100644 index 0000000..add58e7 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.overlay.DebugOverlayGui; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugOverlayGui.class) +public class MixinDebugOverlayGui { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java index 60fc69a..ffa0fd5 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge115; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MainMenuScreen; import net.minecraft.client.gui.screen.Screen; @@ -32,16 +34,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MainMenuScreen.class) public class MixinMainMenuScreen extends Screen { - public MixinMainMenuScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMainMenuScreen(ITextComponent title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge115.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addButton(new Button(5, 6, 98, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java index 57408dd..71a4e7f 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge115; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MultiplayerScreen; import net.minecraft.client.gui.screen.Screen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MultiplayerScreen.class) public class MixinMultiplayerScreen extends Screen { - protected MixinMultiplayerScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMultiplayerScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addButton(new Button(5, 6, 98, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java index a9dd995..d34061f 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java @@ -18,22 +18,89 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NettyEncryptingDecoder; +import net.minecraft.network.NettyEncryptingEncoder; import net.minecraft.network.NetworkManager; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.util.CryptManager; +import net.minecraft.util.LazyValue; +import net.minecraft.util.text.ITextComponent; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import java.net.InetAddress; @Mixin(NetworkManager.class) -public class MixinNetworkManager { +public class MixinNetworkManager implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetAddress p_181124_0_, int p_181124_1_, boolean p_181124_2_, CallbackInfoReturnable cir, NetworkManager networkmanager, Class oclass, LazyValue lazyvalue) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) networkmanager).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(SecretKey key, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = CryptManager.getCipher(2, key); + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new NettyEncryptingEncoder(CryptManager.getCipher(1, key))); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(ITextComponent p_150718_1_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_setCompressionTreshold_1_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new NettyEncryptingDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java index 86826ed..0bca0b1 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NetworkManager; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.NetworkManager$1", remap = false) +@Mixin(targets = "net.minecraft.network.NetworkManager$1") public class MixinNetworkManager_1 { + @Final + @Mutable + NetworkManager val$networkmanager; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$networkmanager); } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..e62390e --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundNBT; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundNBT;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundNBT compoundnbt) { + if (viaforge_version != null) { + compoundnbt.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundNBT compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java index 63bfca1..e26166a 100644 --- a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java @@ -17,7 +17,9 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ServerListScreen; @@ -31,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ServerListScreen.class) public class MixinServerListScreen extends Screen { - protected MixinServerListScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinServerListScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addButton(new Button(5, 6, 98, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, "ViaForge", b -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java new file mode 100644 index 0000000..b92df76 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.network.ServerPinger; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetAddress; + +@Mixin(ServerPinger.class) +public class MixinServerPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc115/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc115/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc115/src/main/resources/mixins.viaforge-mc115.json b/viaforge-mc115/src/main/resources/mixins.viaforge-mc115.json index 78e16aa..ada0467 100644 --- a/viaforge-mc115/src/main/resources/mixins.viaforge-mc115.json +++ b/viaforge-mc115/src/main/resources/mixins.viaforge-mc115.json @@ -4,11 +4,17 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinAddServerScreen", + "MixinClientLoginNetHandler", + "MixinConnectingScreen_1", + "MixinDebugOverlayGui", "MixinMainMenuScreen", "MixinMultiplayerScreen", "MixinNetworkManager", "MixinNetworkManager_1", + "MixinServerData", "MixinServerListScreen", + "MixinServerPinger", "fixes.MixinClientPlayerEntity" ], "injectors": { diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/ViaForge116.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/ViaForge116.java index a2dcc60..27e5be9 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/ViaForge116.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/ViaForge116.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.client.Minecraft; +import net.minecraft.util.Session; import net.minecraft.util.SharedConstants; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge116 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final Session session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 8c5af1c..4d19471 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -18,60 +18,108 @@ package de.florianmichael.viaforge.gui; import com.mojang.blaze3d.matrix.MatrixStack; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.button.Button; import net.minecraft.client.gui.widget.list.AbstractList; import net.minecraft.client.gui.widget.list.ExtendedList; -import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { - super(ITextComponent.nullToEmpty("ViaForge Protocol Selector")); + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { + super(new StringTextComponent("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addButton(new Button(5, height - 25, 20, 20, new StringTextComponent("<-"), b -> minecraft.setScreen(parent))); + if (!this.simple) { + addButton(new Button(width - 105, 5, 100, 20, new StringTextComponent("Create dump"), b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(TextFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(TextFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + })); + addButton(new Button(width - 105, height - 25, 100, 20, new StringTextComponent("Reload configs"), b -> Via.getManager().getConfigurationProvider().reloadConfigs())); + } - addWidget(slotList = new SlotList(minecraft, width, height, 32, height - 32, 10)); - addButton(new Button(width / 2 - 100, height - 27, 200, 20, ITextComponent.nullToEmpty("Back"), b -> minecraft.setScreen(parent))); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); } @Override - public void render(MatrixStack p_230430_1_, int p_230430_2_, int p_230430_3_, float p_230430_4_) { - renderBackground(p_230430_1_); - this.slotList.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); + } - super.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + @Override + public void render(MatrixStack matrices, int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + + renderBackground(matrices); + this.list.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); + + super.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(p_230430_1_, this.font, ITextComponent.nullToEmpty(TextFormatting.GOLD + "ViaForge"), this.width / 4, 6, 16777215); + drawCenteredString(matrices, font, TextFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(p_230430_1_, this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(p_230430_1_, this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(matrices, font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(matrices, font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ExtendedList { + class SlotList extends ExtendedList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); @@ -87,16 +135,29 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override - public void render(MatrixStack p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(p_230432_1_, Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? TextFormatting.GREEN.toString() : TextFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public void render(MatrixStack matrices, int p_230432_2_, int y, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? TextFormatting.GOLD.toString() : TextFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? TextFormatting.WHITE.toString() : TextFormatting.DARK_RED.toString(); + } + + drawCenteredString(matrices, Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java new file mode 100644 index 0000000..cc5e478 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinAddServerScreen.java @@ -0,0 +1,65 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.screen.AddServerScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AddServerScreen.class) +public class MixinAddServerScreen extends Screen { + + @Shadow @Final private ServerData serverData; + + public MixinAddServerScreen(ITextComponent title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addButton(new Button(pos.key(), pos.value(), 100, 20, new StringTextComponent(target != null ? target.getName() : "Set Version"), b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + })); + } + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java new file mode 100644 index 0000000..4e762a5 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinClientLoginNetHandler.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.network.login.ClientLoginNetHandler; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientLoginNetHandler.class) +public class MixinClientLoginNetHandler { + + @Shadow @Final private NetworkManager connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java new file mode 100644 index 0000000..c0b3129 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectingScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetAddress; + +@Mixin(targets = "net.minecraft.client.gui.screen.ConnectingScreen$1") +public class MixinConnectingScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java new file mode 100644 index 0000000..add58e7 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugOverlayGui.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.overlay.DebugOverlayGui; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugOverlayGui.class) +public class MixinDebugOverlayGui { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java index 62b4055..88dcca0 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMainMenuScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge116; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MainMenuScreen; import net.minecraft.client.gui.screen.Screen; @@ -33,16 +35,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MainMenuScreen.class) public class MixinMainMenuScreen extends Screen { - public MixinMainMenuScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMainMenuScreen(ITextComponent title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge116.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addButton(new Button(5, 6, 98, 20, new StringTextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, new StringTextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java index 54e74c7..929589f 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinMultiplayerScreen.java @@ -17,12 +17,15 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.MultiplayerScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.button.Button; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -31,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MultiplayerScreen.class) public class MixinMultiplayerScreen extends Screen { - protected MixinMultiplayerScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinMultiplayerScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addButton(new Button(5, 6, 98, 20, ITextComponent.nullToEmpty("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, new StringTextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java index a9dd995..61a4845 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager.java @@ -18,22 +18,89 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NettyEncryptingDecoder; +import net.minecraft.network.NettyEncryptingEncoder; import net.minecraft.network.NetworkManager; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.util.CryptManager; +import net.minecraft.util.LazyValue; +import net.minecraft.util.text.ITextComponent; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import java.net.InetAddress; @Mixin(NetworkManager.class) -public class MixinNetworkManager { +public class MixinNetworkManager implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetAddress p_181124_0_, int p_181124_1_, boolean p_181124_2_, CallbackInfoReturnable cir, NetworkManager networkmanager, Class oclass, LazyValue lazyvalue) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) networkmanager).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(Cipher p_244777_1_, Cipher p_244777_2_, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = p_244777_1_; + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new NettyEncryptingEncoder(p_244777_2_)); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(ITextComponent p_150718_1_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_setCompressionTreshold_1_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new NettyEncryptingDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java index 86826ed..0bca0b1 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinNetworkManager_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.NetworkManager; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.NetworkManager$1", remap = false) +@Mixin(targets = "net.minecraft.network.NetworkManager$1") public class MixinNetworkManager_1 { + @Final + @Mutable + NetworkManager val$networkmanager; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$networkmanager); } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..e62390e --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundNBT; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundNBT;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundNBT compoundnbt) { + if (viaforge_version != null) { + compoundnbt.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundNBT compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java index 8ed0803..7ace99a 100644 --- a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerListScreen.java @@ -17,13 +17,15 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge116; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ServerListScreen; import net.minecraft.client.gui.widget.button.Button; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -32,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ServerListScreen.class) public class MixinServerListScreen extends Screen { - protected MixinServerListScreen(ITextComponent p_i51108_1_) { - super(p_i51108_1_); + public MixinServerListScreen(ITextComponent title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addButton(new Button(5, 6, 98, 20, ITextComponent.nullToEmpty("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addButton(new Button(pos.key(), pos.value(), 100, 20, new StringTextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java new file mode 100644 index 0000000..b8bdbe0 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/mixin/MixinServerPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.network.ServerPinger; +import net.minecraft.network.NetworkManager; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetAddress; + +@Mixin(ServerPinger.class) +public class MixinServerPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, Runnable p_147224_2_, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;connectToServer(Ljava/net/InetAddress;IZ)Lnet/minecraft/network/NetworkManager;")) + public NetworkManager trackVersion(InetAddress address, int i, boolean b) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return NetworkManager.connectToServer(address, i, b); + } +} diff --git a/viaforge-mc116/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc116/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc116/src/main/resources/mixins.viaforge-mc116.json b/viaforge-mc116/src/main/resources/mixins.viaforge-mc116.json index 3da5589..5d2b000 100644 --- a/viaforge-mc116/src/main/resources/mixins.viaforge-mc116.json +++ b/viaforge-mc116/src/main/resources/mixins.viaforge-mc116.json @@ -4,11 +4,17 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinAddServerScreen", + "MixinClientLoginNetHandler", + "MixinConnectingScreen_1", + "MixinDebugOverlayGui", "MixinMainMenuScreen", "MixinMultiplayerScreen", "MixinNetworkManager", "MixinNetworkManager_1", + "MixinServerData", "MixinServerListScreen", + "MixinServerPinger", "fixes.MixinClientPlayerEntity" ], "injectors": { diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/ViaForge117.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/ViaForge117.java index 0dbd60e..7ab37d0 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/ViaForge117.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/ViaForge117.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; +import net.minecraft.client.User; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge117 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final User session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 40630b4..ec36b5c 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -18,8 +18,10 @@ package de.florianmichael.viaforge.gui; import com.mojang.blaze3d.vertex.PoseStack; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; -import net.minecraft.ChatFormatting; +import net.lenni0451.mcstructs.core.TextFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ObjectSelectionList; @@ -27,51 +29,97 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(new TextComponent("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addRenderableWidget(new Button(5, height - 25, 20, 20, new TextComponent("<-"), b -> minecraft.setScreen(parent))); + if (!this.simple) { + addRenderableWidget(new Button(width - 105, 5, 100, 20, new TextComponent("Create dump"), b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(TextFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(TextFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + })); + addRenderableWidget(new Button(width - 105, height - 25, 100, 20, new TextComponent("Reload configs"), b -> Via.getManager().getConfigurationProvider().reloadConfigs())); + } - addWidget(slotList = new SlotList(minecraft, width, height, 32, height - 32, 10)); - addRenderableWidget(new Button(width / 2 - 100, height - 27, 200, 20, new TextComponent("Back"), b -> minecraft.setScreen(parent))); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); } @Override - public void render(PoseStack p_230430_1_, int p_230430_2_, int p_230430_3_, float p_230430_4_) { - renderBackground(p_230430_1_); - this.slotList.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); + } - super.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + @Override + public void render(PoseStack matrices, int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + + renderBackground(matrices); + this.list.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); + + super.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(p_230430_1_, this.font, new TextComponent(ChatFormatting.GOLD + "ViaForge"), this.width / 4, 6, 16777215); + drawCenteredString(matrices, font, TextFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(p_230430_1_, this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(p_230430_1_, this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(matrices, font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(matrices, font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ObjectSelectionList { + class SlotList extends ObjectSelectionList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); @@ -87,21 +135,34 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); - } - - @Override - public void render(PoseStack p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(p_230432_1_, Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? ChatFormatting.GREEN.toString() : ChatFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override public Component getNarration() { return new TextComponent(versionEnum.getName()); } + + @Override + public void render(PoseStack matrices, int p_93524_, int y, int p_93526_, int p_93527_, int p_93528_, int p_93529_, int p_93530_, boolean p_93531_, float p_93532_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? TextFormatting.GOLD.toString() : TextFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? TextFormatting.WHITE.toString() : TextFormatting.DARK_RED.toString(); + } + + drawCenteredString(matrices, Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); + } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java new file mode 100644 index 0000000..741a694 --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientHandshakePacketListenerImpl.class) +public class MixinClientHandshakePacketListenerImpl { + + @Shadow @Final private Connection connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java new file mode 100644 index 0000000..b2e5703 --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetSocketAddress; + +@Mixin(targets = "net.minecraft.client.gui.screens.ConnectScreen$1") +public class MixinConnectScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java index ca933b2..5f1483e 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java @@ -18,22 +18,86 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.CipherDecoder; +import net.minecraft.network.CipherEncoder; import net.minecraft.network.Connection; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.network.chat.Component; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import java.net.InetSocketAddress; @Mixin(Connection.class) -public class MixinConnection { +public class MixinConnection implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetSocketAddress p_178301_, boolean p_178302_, CallbackInfoReturnable cir, final Connection connection) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) connection).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(Cipher p_244777_1_, Cipher p_244777_2_, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = p_244777_1_; + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new CipherEncoder(p_244777_2_)); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(Component p_129508_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_129485_, boolean p_182682_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new CipherDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java index 9988dd8..5bef153 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.Connection$1", remap = false) +@Mixin(targets = "net.minecraft.network.Connection$1") public class MixinConnection_1 { + @Final + @Mutable + Connection val$connection; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$connection); } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java new file mode 100644 index 0000000..ef1533c --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugScreenOverlay.class) +public class MixinDebugScreenOverlay { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java index c2b3cf5..07d4d0a 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge117; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.DirectJoinServerScreen; @@ -33,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(DirectJoinServerScreen.class) public class MixinDirectJoinServerScreen extends Screen { - protected MixinDirectJoinServerScreen(Component p_96550_) { - super(p_96550_); + public MixinDirectJoinServerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java new file mode 100644 index 0000000..2164cc7 --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java @@ -0,0 +1,67 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.EditServerScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EditServerScreen.class) +public class MixinEditServerScreen extends Screen { + + @Shadow + @Final + private ServerData serverData; + + public MixinEditServerScreen(Component title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent(target != null ? target.getName() : "Set Version"), b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + })); + } + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java index bc7e280..17ad42a 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge117; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -33,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(JoinMultiplayerScreen.class) public class MixinJoinMultiplayerScreen extends Screen { - protected MixinJoinMultiplayerScreen(Component p_96550_) { - super(p_96550_); + public MixinJoinMultiplayerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..6061579 --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundTag; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundTag compoundtag) { + if (viaforge_version != null) { + compoundtag.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundTag compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java new file mode 100644 index 0000000..ab43c3f --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.ServerStatusPinger; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetSocketAddress; + +@Mixin(ServerStatusPinger.class) +public class MixinServerStatusPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, Runnable p_147224_2_, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java index fc1e630..bb4d0d9 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge117; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -33,16 +35,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(TitleScreen.class) public class MixinTitleScreen extends Screen { - protected MixinTitleScreen(Component p_96550_) { - super(p_96550_); + public MixinTitleScreen(Component title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge117.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java index a27f4ed..24f5235 100644 --- a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java @@ -34,4 +34,4 @@ public class MixinLocalPlayer extends AbstractClientPlayer { } return lastOnGround; } -} +} \ No newline at end of file diff --git a/viaforge-mc117/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc117/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc117/src/main/resources/mixins.viaforge-mc117.json b/viaforge-mc117/src/main/resources/mixins.viaforge-mc117.json index 0bcab49..877f780 100644 --- a/viaforge-mc117/src/main/resources/mixins.viaforge-mc117.json +++ b/viaforge-mc117/src/main/resources/mixins.viaforge-mc117.json @@ -4,15 +4,24 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinClientHandshakePacketListenerImpl", "MixinConnection", "MixinConnection_1", + "MixinDebugScreenOverlay", "MixinDirectJoinServerScreen", + "MixinEditServerScreen", "MixinJoinMultiplayerScreen", + "MixinServerData", + "MixinServerStatusPinger", "MixinTitleScreen", "fixes.MixinLocalPlayer" ], "injectors": { "defaultRequire": 1 }, - "refmap": "mixins.viaforge-mc117.refmap.json" + "refmap": "mixins.viaforge-mc117.refmap.json", + "mixins": [ + "MixinConnection_1", + "MixinConnectScreen_1" + ] } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/ViaForge118.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/ViaForge118.java index 4326af6..3330024 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/ViaForge118.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/ViaForge118.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; +import net.minecraft.client.User; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge118 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final User session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 40630b4..f6668e5 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -18,8 +18,10 @@ package de.florianmichael.viaforge.gui; import com.mojang.blaze3d.vertex.PoseStack; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; -import net.minecraft.ChatFormatting; +import net.lenni0451.mcstructs.core.TextFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ObjectSelectionList; @@ -27,58 +29,104 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(new TextComponent("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addRenderableWidget(new Button(5, height - 25, 20, 20, new TextComponent("<-"), b -> minecraft.setScreen(parent))); + if (!this.simple) { + addRenderableWidget(new Button(width - 105, 5, 100, 20, new TextComponent("Create dump"), b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(TextFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(TextFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + })); + addRenderableWidget(new Button(width - 105, height - 25, 100, 20, new TextComponent("Reload configs"), b -> Via.getManager().getConfigurationProvider().reloadConfigs())); + } - addWidget(slotList = new SlotList(minecraft, width, height, 32, height - 32, 10)); - addRenderableWidget(new Button(width / 2 - 100, height - 27, 200, 20, new TextComponent("Back"), b -> minecraft.setScreen(parent))); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); } @Override - public void render(PoseStack p_230430_1_, int p_230430_2_, int p_230430_3_, float p_230430_4_) { - renderBackground(p_230430_1_); - this.slotList.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); + } - super.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + @Override + public void render(PoseStack matrices, int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + + renderBackground(matrices); + this.list.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); + + super.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(p_230430_1_, this.font, new TextComponent(ChatFormatting.GOLD + "ViaForge"), this.width / 4, 6, 16777215); + drawCenteredString(matrices, font, TextFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(p_230430_1_, this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(p_230430_1_, this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(matrices, font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(matrices, font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ObjectSelectionList { + class SlotList extends ObjectSelectionList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); } } - public class SlotEntry extends ObjectSelectionList.Entry { + public class SlotEntry extends Entry { private final VersionEnum versionEnum; @@ -87,21 +135,34 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); - } - - @Override - public void render(PoseStack p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(p_230432_1_, Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? ChatFormatting.GREEN.toString() : ChatFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override public Component getNarration() { return new TextComponent(versionEnum.getName()); } + + @Override + public void render(PoseStack matrices, int p_93524_, int y, int p_93526_, int p_93527_, int p_93528_, int p_93529_, int p_93530_, boolean p_93531_, float p_93532_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? TextFormatting.GOLD.toString() : TextFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? TextFormatting.WHITE.toString() : TextFormatting.DARK_RED.toString(); + } + + drawCenteredString(matrices, Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); + } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java new file mode 100644 index 0000000..741a694 --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientHandshakePacketListenerImpl.class) +public class MixinClientHandshakePacketListenerImpl { + + @Shadow @Final private Connection connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java new file mode 100644 index 0000000..b2e5703 --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetSocketAddress; + +@Mixin(targets = "net.minecraft.client.gui.screens.ConnectScreen$1") +public class MixinConnectScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java index ca933b2..5f1483e 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java @@ -18,22 +18,86 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.CipherDecoder; +import net.minecraft.network.CipherEncoder; import net.minecraft.network.Connection; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.network.chat.Component; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import java.net.InetSocketAddress; @Mixin(Connection.class) -public class MixinConnection { +public class MixinConnection implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetSocketAddress p_178301_, boolean p_178302_, CallbackInfoReturnable cir, final Connection connection) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) connection).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(Cipher p_244777_1_, Cipher p_244777_2_, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = p_244777_1_; + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new CipherEncoder(p_244777_2_)); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(Component p_129508_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_129485_, boolean p_182682_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new CipherDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java index 9988dd8..5bef153 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.Connection$1", remap = false) +@Mixin(targets = "net.minecraft.network.Connection$1") public class MixinConnection_1 { + @Final + @Mutable + Connection val$connection; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$connection); } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java new file mode 100644 index 0000000..ef1533c --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugScreenOverlay.class) +public class MixinDebugScreenOverlay { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java index a9d420c..07d4d0a 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge118; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.DirectJoinServerScreen; @@ -33,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(DirectJoinServerScreen.class) public class MixinDirectJoinServerScreen extends Screen { - protected MixinDirectJoinServerScreen(Component p_96550_) { - super(p_96550_); + public MixinDirectJoinServerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java new file mode 100644 index 0000000..2164cc7 --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java @@ -0,0 +1,67 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.EditServerScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EditServerScreen.class) +public class MixinEditServerScreen extends Screen { + + @Shadow + @Final + private ServerData serverData; + + public MixinEditServerScreen(Component title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent(target != null ? target.getName() : "Set Version"), b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + })); + } + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java index b0c23fa..17ad42a 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge118; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -33,14 +34,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(JoinMultiplayerScreen.class) public class MixinJoinMultiplayerScreen extends Screen { - protected MixinJoinMultiplayerScreen(Component p_96550_) { - super(p_96550_); + public MixinJoinMultiplayerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this)))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..6061579 --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundTag; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundTag compoundtag) { + if (viaforge_version != null) { + compoundtag.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundTag compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java new file mode 100644 index 0000000..ab43c3f --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.ServerStatusPinger; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetSocketAddress; + +@Mixin(ServerStatusPinger.class) +public class MixinServerStatusPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, Runnable p_147224_2_, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java index 82b854b..a85f1ef 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge118; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -33,16 +35,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(TitleScreen.class) public class MixinTitleScreen extends Screen { - protected MixinTitleScreen(Component p_96550_) { - super(p_96550_); + public MixinTitleScreen(Component title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge118.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addRenderableWidget(new Button(5, 6, 98, 20, new TextComponent("ViaForge"), b -> GuiProtocolSelector.open(minecraft))); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(new Button(pos.key(), pos.value(), 100, 20, new TextComponent("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft))); } } } diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java index a27f4ed..24f5235 100644 --- a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java @@ -34,4 +34,4 @@ public class MixinLocalPlayer extends AbstractClientPlayer { } return lastOnGround; } -} +} \ No newline at end of file diff --git a/viaforge-mc118/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc118/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc118/src/main/resources/mixins.viaforge-mc118.json b/viaforge-mc118/src/main/resources/mixins.viaforge-mc118.json index 5df2f08..2115676 100644 --- a/viaforge-mc118/src/main/resources/mixins.viaforge-mc118.json +++ b/viaforge-mc118/src/main/resources/mixins.viaforge-mc118.json @@ -4,10 +4,16 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinClientHandshakePacketListenerImpl", "MixinConnection", "MixinConnection_1", + "MixinConnectScreen_1", + "MixinDebugScreenOverlay", "MixinDirectJoinServerScreen", + "MixinEditServerScreen", "MixinJoinMultiplayerScreen", + "MixinServerData", + "MixinServerStatusPinger", "MixinTitleScreen", "fixes.MixinLocalPlayer" ], diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/ViaForge119.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/ViaForge119.java index a763418..5fe176e 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/ViaForge119.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/ViaForge119.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; +import net.minecraft.client.User; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge119 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final User session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getGameProfile(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 9f40f36..56aabd3 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -18,66 +18,114 @@ package de.florianmichael.viaforge.gui; import com.mojang.blaze3d.vertex.PoseStack; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; -import net.minecraft.ChatFormatting; +import net.lenni0451.mcstructs.core.TextFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ObjectSelectionList; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(Component.literal("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addRenderableWidget(Button.builder(Component.literal("<-"), b -> minecraft.setScreen(parent)).bounds(5, height - 25, 20, 20).build()); + if (!this.simple) { + addRenderableWidget(Button.builder(Component.literal("Create dump"), b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(TextFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(TextFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + }).bounds(width - 105, 5, 100, 20).build()); + addRenderableWidget(Button.builder(Component.literal("Reload configs"), b -> Via.getManager().getConfigurationProvider().reloadConfigs()).bounds(width - 105, height - 25, 100, 20).build()); + } - addWidget(slotList = new SlotList(minecraft, width, height, 32, height - 32, 10)); - addRenderableWidget(new Button.Builder(Component.literal("Back"), b -> minecraft.setScreen(parent)).bounds(width / 2 - 100, height - 27, 200, 20).build()); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); } @Override - public void render(PoseStack p_230430_1_, int p_230430_2_, int p_230430_3_, float p_230430_4_) { - renderBackground(p_230430_1_); - this.slotList.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); + } - super.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + @Override + public void render(PoseStack matrices, int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + + renderBackground(matrices); + this.list.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); + + super.render(matrices, p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - drawCenteredString(p_230430_1_, this.font, Component.literal(ChatFormatting.GOLD + "ViaForge"), this.width / 4, 6, 16777215); + drawCenteredString(matrices, font, TextFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - drawString(p_230430_1_, this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - drawString(p_230430_1_, this.font, "Discord: florianmichael", 1, 11, -1); + drawCenteredString(matrices, font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + drawString(matrices, font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ObjectSelectionList { + class SlotList extends ObjectSelectionList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); } } - public class SlotEntry extends ObjectSelectionList.Entry { + public class SlotEntry extends Entry { private final VersionEnum versionEnum; @@ -86,21 +134,34 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); - } - - @Override - public void render(PoseStack p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - drawCenteredString(p_230432_1_, Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? ChatFormatting.GREEN.toString() : ChatFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override public Component getNarration() { return Component.literal(versionEnum.getName()); } + + @Override + public void render(PoseStack matrices, int p_93524_, int y, int p_93526_, int p_93527_, int p_93528_, int p_93529_, int p_93530_, boolean p_93531_, float p_93532_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? TextFormatting.GOLD.toString() : TextFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? TextFormatting.WHITE.toString() : TextFormatting.DARK_RED.toString(); + } + + drawCenteredString(matrices, Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); + } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java new file mode 100644 index 0000000..741a694 --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java @@ -0,0 +1,37 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientHandshakePacketListenerImpl.class) +public class MixinClientHandshakePacketListenerImpl { + + @Shadow @Final private Connection connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Lcom/mojang/authlib/GameProfile;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(profile, authenticationToken, serverId); + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java new file mode 100644 index 0000000..b2e5703 --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnectScreen_1.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetSocketAddress; + +@Mixin(targets = "net.minecraft.client.gui.screens.ConnectScreen$1") +public class MixinConnectScreen_1 { + + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java index ca933b2..5f1483e 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java @@ -18,22 +18,86 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.CipherDecoder; +import net.minecraft.network.CipherEncoder; import net.minecraft.network.Connection; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.network.chat.Component; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import java.net.InetSocketAddress; @Mixin(Connection.class) -public class MixinConnection { +public class MixinConnection implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;group(Lio/netty/channel/EventLoopGroup;)Lio/netty/bootstrap/AbstractBootstrap;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetSocketAddress p_178301_, boolean p_178302_, CallbackInfoReturnable cir, final Connection connection) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) connection).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(Cipher p_244777_1_, Cipher p_244777_2_, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = p_244777_1_; + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new CipherEncoder(p_244777_2_)); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(Component p_129508_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_129485_, boolean p_182682_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new CipherDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java index 9988dd8..5bef153 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.Connection$1", remap = false) +@Mixin(targets = "net.minecraft.network.Connection$1") public class MixinConnection_1 { + @Final + @Mutable + Connection val$connection; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$connection); } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java new file mode 100644 index 0000000..ef1533c --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugScreenOverlay.class) +public class MixinDebugScreenOverlay { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java index f53089f..09053d7 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge119; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.DirectJoinServerScreen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(DirectJoinServerScreen.class) public class MixinDirectJoinServerScreen extends Screen { - protected MixinDirectJoinServerScreen(Component p_96550_) { - super(p_96550_); + public MixinDirectJoinServerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), b -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java new file mode 100644 index 0000000..80f1697 --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java @@ -0,0 +1,66 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.EditServerScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.network.chat.Component; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EditServerScreen.class) +public class MixinEditServerScreen extends Screen { + + @Shadow + @Final + private ServerData serverData; + + public MixinEditServerScreen(Component title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addRenderableWidget(Button.builder(Component.literal(target != null ? target.getName() : "Set Version"), b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + }).bounds(pos.key(), pos.value(), 100, 20).build()); + } + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java index 4b3263c..eba26e1 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge119; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(JoinMultiplayerScreen.class) public class MixinJoinMultiplayerScreen extends Screen { - protected MixinJoinMultiplayerScreen(Component p_96550_) { - super(p_96550_); + public MixinJoinMultiplayerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..6061579 --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundTag; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundTag compoundtag) { + if (viaforge_version != null) { + compoundtag.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundTag compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java new file mode 100644 index 0000000..ab43c3f --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java @@ -0,0 +1,50 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.ServerStatusPinger; +import net.minecraft.network.Connection; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetSocketAddress; + +@Mixin(ServerStatusPinger.class) +public class MixinServerStatusPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, Runnable p_147224_2_, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;Z)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress oclass, boolean lazyloadedvalue) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return Connection.connectToServer(oclass, lazyloadedvalue); + } +} diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java index 513edfb..a5516c5 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge119; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -32,16 +34,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(TitleScreen.class) public class MixinTitleScreen extends Screen { - protected MixinTitleScreen(Component p_96550_) { - super(p_96550_); + public MixinTitleScreen(Component title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge119.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java index a27f4ed..24f5235 100644 --- a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java @@ -34,4 +34,4 @@ public class MixinLocalPlayer extends AbstractClientPlayer { } return lastOnGround; } -} +} \ No newline at end of file diff --git a/viaforge-mc119/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..2d06f53 --- /dev/null +++ b/viaforge-mc119/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,57 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY, ""); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, Agent.MINECRAFT, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(com.mojang.authlib.GameProfile profile, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final com.mojang.authlib.GameProfile inProfile = new com.mojang.authlib.GameProfile(uuid, null); + final com.mojang.authlib.GameProfile mojangProfile = SESSION_SERVICE.fillProfileProperties(inProfile, true); + if (mojangProfile.equals(inProfile)) throw new ProfileNotFoundException(); + + final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId()); + for (final java.util.Map.Entry entry : mojangProfile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature())); + } + + return gameProfile; + } +} diff --git a/viaforge-mc119/src/main/resources/mixins.viaforge-mc119.json b/viaforge-mc119/src/main/resources/mixins.viaforge-mc119.json index 609617c..de5b42a 100644 --- a/viaforge-mc119/src/main/resources/mixins.viaforge-mc119.json +++ b/viaforge-mc119/src/main/resources/mixins.viaforge-mc119.json @@ -4,10 +4,16 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinClientHandshakePacketListenerImpl", "MixinConnection", "MixinConnection_1", + "MixinConnectScreen_1", + "MixinDebugScreenOverlay", "MixinDirectJoinServerScreen", + "MixinEditServerScreen", "MixinJoinMultiplayerScreen", + "MixinServerData", + "MixinServerStatusPinger", "MixinTitleScreen", "fixes.MixinLocalPlayer" ], diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/ViaForge120.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/ViaForge120.java index f5b687e..d61402e 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/ViaForge120.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/ViaForge120.java @@ -18,9 +18,12 @@ package de.florianmichael.viaforge; import de.florianmichael.viaforge.common.platform.VFPlatform; +import de.florianmichael.viaforge.provider.ViaForgeGameProfileFetcher; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; +import net.minecraft.client.User; import net.minecraftforge.fml.common.Mod; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; import java.util.function.Supplier; @@ -44,4 +47,16 @@ public class ViaForge120 implements VFPlatform { public File getLeadingDirectory() { return Minecraft.getInstance().gameDirectory; } + + @Override + public void joinServer(String serverId) throws Throwable { + final User session = Minecraft.getInstance().getUser(); + + Minecraft.getInstance().getMinecraftSessionService().joinServer(session.getProfileId(), session.getAccessToken(), serverId); + } + + @Override + public GameProfileFetcher getGameProfileFetcher() { + return new ViaForgeGameProfileFetcher(); + } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java index 87be5b4..6ef71a3 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/gui/GuiProtocolSelector.java @@ -17,8 +17,11 @@ */ package de.florianmichael.viaforge.gui; +import com.mojang.blaze3d.vertex.PoseStack; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.util.DumpUtil; import de.florianmichael.viaforge.common.ViaForgeCommon; -import net.minecraft.ChatFormatting; +import net.lenni0451.mcstructs.core.TextFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; @@ -26,57 +29,102 @@ import net.minecraft.client.gui.components.ObjectSelectionList; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.raphimc.vialoader.util.VersionEnum; +import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + public class GuiProtocolSelector extends Screen { - private final Screen parent; + public final Screen parent; + public final boolean simple; + public final FinishedCallback finishedCallback; + + private SlotList list; + + private String status; + private long time; public static void open(final Minecraft minecraft) { // Bypass for some weird bytecode instructions errors in Forge minecraft.setScreen(new GuiProtocolSelector(minecraft.screen)); } - private SlotList slotList; + public GuiProtocolSelector(final Screen parent) { + this(parent, false, (version, unused) -> { + // Default action is to set the target version and go back to the parent screen. + ViaForgeCommon.getManager().setTargetVersion(version); + }); + } - public GuiProtocolSelector(Screen parent) { + public GuiProtocolSelector(final Screen parent, final boolean simple, final FinishedCallback finishedCallback) { super(Component.literal("ViaForge Protocol Selector")); this.parent = parent; + this.simple = simple; + this.finishedCallback = finishedCallback; } @Override - protected void init() { + public void init() { super.init(); + addRenderableWidget(Button.builder(Component.literal("<-"), b -> minecraft.setScreen(parent)).bounds(5, height - 25, 20, 20).build()); + if (!this.simple) { + addRenderableWidget(Button.builder(Component.literal("Create dump"), b -> { + try { + minecraft.keyboardHandler.setClipboard(DumpUtil.postDump(UUID.randomUUID()).get()); + setStatus(TextFormatting.GREEN + "Dump created and copied to clipboard"); + } catch (InterruptedException | ExecutionException e) { + setStatus(TextFormatting.RED + "Failed to create dump: " + e.getMessage()); + } + }).bounds(width - 105, 5, 100, 20).build()); + addRenderableWidget(Button.builder(Component.literal("Reload configs"), b -> Via.getManager().getConfigurationProvider().reloadConfigs()).bounds(width - 105, height - 25, 100, 20).build()); + } - addWidget(slotList = new SlotList(minecraft, width, height, 32, height - 32, 10)); - addRenderableWidget(new Button.Builder(Component.literal("Back"), b -> minecraft.setScreen(parent)).bounds(width / 2 - 100, height - 27, 200, 20).build()); + list = new SlotList(minecraft, width, height, 3 + 3 /* start offset */ + (font.lineHeight + 2) * 3 /* title is 2 */, height - 30, font.lineHeight + 2); + } + + public void setStatus(final String status) { + this.status = status; + this.time = System.currentTimeMillis(); } @Override - public void render(GuiGraphics p_230430_1_, int p_230430_2_, int p_230430_3_, float p_230430_4_) { - this.slotList.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + public boolean keyPressed(int keyCode, int scanCode, int actions) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + minecraft.setScreen(parent); + } + return super.keyPressed(keyCode, scanCode, actions); + } - super.render(p_230430_1_, p_230430_2_, p_230430_3_, p_230430_4_); + @Override + public void render(GuiGraphics graphics, int p_230430_2_, int p_230430_3_, float p_230430_4_) { + if (System.currentTimeMillis() - this.time >= 10_000) { + this.status = null; + } + + this.list.render(graphics, p_230430_2_, p_230430_3_, p_230430_4_); + super.render(graphics, p_230430_2_, p_230430_3_, p_230430_4_); GL11.glPushMatrix(); GL11.glScalef(2.0F, 2.0F, 2.0F); - p_230430_1_.drawCenteredString(this.font, Component.literal(ChatFormatting.GOLD + "ViaForge"), this.width / 4, 6, 16777215); + graphics.drawCenteredString(font, TextFormatting.GOLD + "ViaForge", width / 4, 3, 16777215); GL11.glPopMatrix(); - p_230430_1_.drawString(this.font, "by https://github.com/ViaVersion/ViaForge", 1, 1, -1); - p_230430_1_.drawString(this.font, "Discord: florianmichael", 1, 11, -1); + graphics.drawCenteredString(font, "https://github.com/ViaVersion/ViaForge", width / 2, (font.lineHeight + 2) * 2 + 3, -1); + graphics.drawString(font, status != null ? status : "Discord: florianmichael", 3, 3, -1); } - static class SlotList extends ObjectSelectionList { + class SlotList extends ObjectSelectionList { - public SlotList(Minecraft p_i51146_1_, int p_i51146_2_, int p_i51146_3_, int p_i51146_4_, int p_i51146_5_, int p_i51146_6_) { - super(p_i51146_1_, p_i51146_2_, p_i51146_3_, p_i51146_4_, p_i51146_5_, p_i51146_6_); + public SlotList(Minecraft client, int width, int height, int top, int bottom, int slotHeight) { + super(client, width, height, top, bottom, slotHeight); for (VersionEnum version : VersionEnum.SORTED_VERSIONS) { addEntry(new SlotEntry(version)); } } - public class SlotEntry extends ObjectSelectionList.Entry { + public class SlotEntry extends Entry { private final VersionEnum versionEnum; @@ -85,21 +133,34 @@ public class GuiProtocolSelector extends Screen { } @Override - public boolean mouseClicked(double p_231044_1_, double p_231044_3_, int p_231044_5_) { - ViaForgeCommon.getManager().setTargetVersion(versionEnum); - return super.mouseClicked(p_231044_1_, p_231044_3_, p_231044_5_); - } - - @Override - public void render(GuiGraphics p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) { - p_230432_1_.drawCenteredString(Minecraft.getInstance().font, - (ViaForgeCommon.getManager().getTargetVersion() == versionEnum ? ChatFormatting.GREEN.toString() : ChatFormatting.DARK_RED.toString()) + versionEnum.getName(), width / 2, p_230432_3_, -1); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + GuiProtocolSelector.this.finishedCallback.finished(versionEnum, GuiProtocolSelector.this.parent); + return super.mouseClicked(mouseX, mouseY, button); } @Override public Component getNarration() { return Component.literal(versionEnum.getName()); } + + @Override + public void render(GuiGraphics graphics, int p_93524_, int y, int p_93526_, int p_93527_, int p_93528_, int p_93529_, int p_93530_, boolean p_93531_, float p_93532_) { + final VersionEnum targetVersion = ViaForgeCommon.getManager().getTargetVersion(); + + String color; + if (targetVersion == versionEnum) { + color = GuiProtocolSelector.this.simple ? TextFormatting.GOLD.toString() : TextFormatting.GREEN.toString(); + } else { + color = GuiProtocolSelector.this.simple ? TextFormatting.WHITE.toString() : TextFormatting.DARK_RED.toString(); + } + + graphics.drawCenteredString(Minecraft.getInstance().font, color + versionEnum.getName(), width / 2, y, -1); + } } } + + public interface FinishedCallback { + + void finished(final VersionEnum version, final Screen parent); + } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java new file mode 100644 index 0000000..4cc0116 --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinClientHandshakePacketListenerImpl.java @@ -0,0 +1,38 @@ +package de.florianmichael.viaforge.mixin; + +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; +import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.UUID; + +@SuppressWarnings("DataFlowIssue") +@Mixin(ClientHandshakePacketListenerImpl.class) +public class MixinClientHandshakePacketListenerImpl { + + @Shadow @Final private Connection connection; + + @Redirect(method = "authenticateServer", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;joinServer(Ljava/util/UUID;Ljava/lang/String;Ljava/lang/String;)V")) + public void onlyJoinServerIfPremium(MinecraftSessionService instance, UUID uuid, String authenticationToken, String serverId) throws AuthenticationException { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + final UserConnection user = connection.channel().attr(ViaForgeCommon.LOCAL_VIA_USER).get(); + if (user != null && user.has(ProtocolMetadataStorage.class) && !user.get(ProtocolMetadataStorage.class).authenticate) { + // We are in the 1.7 -> 1.6 protocol, so we need to skip the joinServer call + // if the server is in offline mode, due the packet changes <-> networking changes + // Minecraft's networking code is bad for us. + return; + } + } + instance.joinServer(uuid, authenticationToken, serverId); + } +} diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java index ca933b2..3d5b610 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection.java @@ -18,22 +18,105 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.client.Minecraft; +import net.minecraft.network.CipherDecoder; +import net.minecraft.network.CipherEncoder; import net.minecraft.network.Connection; -import net.raphimc.vialoader.netty.CompressionReorderEvent; +import net.minecraft.network.PacketListener; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.handshake.ClientIntent; +import net.minecraft.util.SampleLogger; +import net.raphimc.vialoader.netty.VLLegacyPipeline; +import net.raphimc.vialoader.util.VersionEnum; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import javax.crypto.Cipher; +import java.net.InetSocketAddress; @Mixin(Connection.class) -public class MixinConnection { +public class MixinConnection implements VFNetworkManager { @Shadow private Channel channel; + @Unique + private Cipher viaforge_decryptionCipher; + + @Unique + private VersionEnum viaforge_targetVersion; + + @Inject(method = "connectToServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connect(Ljava/net/InetSocketAddress;ZLnet/minecraft/network/Connection;)Lio/netty/channel/ChannelFuture;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void trackSelfTarget(InetSocketAddress p_178301_, boolean p_178302_, SampleLogger p_300093_, CallbackInfoReturnable cir, Connection connection) { + // The connecting screen and server pinger are setting the main target version when a specific version for a server is set, + // This works for joining perfect since we can simply restore the version when the server doesn't have a specific one set, + // but for the server pinger we need to store the target version and force the pinging to use the target version. + // Due to the fact that the server pinger is being called multiple times. + ((VFNetworkManager) connection).viaforge_setTrackedVersion(ViaForgeCommon.getManager().getTargetVersion()); + } + + @Inject(method = "initiateServerboundConnection", at = @At("HEAD")) + public void test(String p_300730_, int p_300598_, PacketListener p_298739_, ClientIntent p_297789_, CallbackInfo ci) { + if (Minecraft.getInstance().getCurrentServer() instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) Minecraft.getInstance().getCurrentServer()).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + } + } + + @Inject(method = "setEncryptionKey", at = @At("HEAD"), cancellable = true) + private void storeEncryptionCiphers(Cipher p_244777_1_, Cipher p_244777_2_, CallbackInfo ci) { + if (ViaForgeCommon.getManager().getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Minecraft's encryption code is bad for us, we need to reorder the pipeline + ci.cancel(); + + // Minecraft 1.6.4 supports tile encryption which means the server can only disable one side of the encryption + // So we only enable the encryption side and later enable the decryption side if the 1.7 -> 1.6 protocol + // tells us to do, therefore we need to store the cipher instance. + this.viaforge_decryptionCipher = p_244777_1_; + + // Enabling the encryption side + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "encrypt", new CipherEncoder(p_244777_2_)); + } + } + + @Inject(method = "disconnect", at = @At("HEAD")) + public void restoreTargetVersion(Component p_129508_, CallbackInfo ci) { + // If the previous server forced a version, we need to restore the version to the default one. + ViaForgeCommon.getManager().restoreVersion(); + } + @Inject(method = "setupCompression", at = @At("RETURN")) public void reorderPipeline(int p_129485_, boolean p_182682_, CallbackInfo ci) { ViaForgeCommon.getManager().reorderCompression(channel); } + + @Override + public void viaforge_setupPreNettyDecryption() { + // Enabling the decryption side for 1.6.4 if the 1.7 -> 1.6 protocol tells us to do + this.channel.pipeline().addBefore(VLLegacyPipeline.VIALEGACY_PRE_NETTY_LENGTH_REMOVER_NAME, "decrypt", new CipherDecoder(this.viaforge_decryptionCipher)); + } + + @Override + public VersionEnum viaforge_getTrackedVersion() { + return viaforge_targetVersion; + } + + @Override + public void viaforge_setTrackedVersion(VersionEnum version) { + viaforge_targetVersion = version; + } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java index 9988dd8..5bef153 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinConnection_1.java @@ -18,17 +18,25 @@ package de.florianmichael.viaforge.mixin; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.protocolhack.netty.VFNetworkManager; import io.netty.channel.Channel; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(targets = "net.minecraft.network.Connection$1", remap = false) +@Mixin(targets = "net.minecraft.network.Connection$1") public class MixinConnection_1 { + @Final + @Mutable + Connection val$connection; + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) private void onInitChannel(Channel channel, CallbackInfo ci) { - ViaForgeCommon.getManager().inject(channel); + ViaForgeCommon.getManager().inject(channel, (VFNetworkManager) val$connection); } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java new file mode 100644 index 0000000..ef1533c --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDebugScreenOverlay.java @@ -0,0 +1,35 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import net.raphimc.vialoader.util.VersionEnum; +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.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(DebugScreenOverlay.class) +public class MixinDebugScreenOverlay { + + @Inject(method = "getSystemInformation", at = @At(value = "TAIL")) + public void addViaForgeVersion(CallbackInfoReturnable> cir) { + final ViaForgeCommon common = ViaForgeCommon.getManager(); + final VersionEnum version = ViaForgeCommon.getManager().getTargetVersion(); + + if (common.getConfig().isShowProtocolVersionInF3() && version != common.getNativeVersion() && !common.getPlatform().isSingleplayer().get()) { + cir.getReturnValue().add(""); + + int protocolVersion = version.getVersion(); + if (version.isOlderThanOrEqualTo(VersionEnum.r1_6_4)) { + // Older versions (<= 1.6.4) are using fake ids in ViaLegacy to prevent version duplications / mismatches + // So we need to unmap the version to get the real protocol version id + protocolVersion = LegacyProtocolVersion.getRealProtocolVersion(protocolVersion); + } + + cir.getReturnValue().add("ViaForge: " + version.getName() + " (" + protocolVersion + ")"); + } + } +} diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java index def0e7b..09053d7 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinDirectJoinServerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge120; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.DirectJoinServerScreen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(DirectJoinServerScreen.class) public class MixinDirectJoinServerScreen extends Screen { - protected MixinDirectJoinServerScreen(Component p_96550_) { - super(p_96550_); + public MixinDirectJoinServerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowDirectConnectButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowDirectConnectButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), b -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java new file mode 100644 index 0000000..80f1697 --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinEditServerScreen.java @@ -0,0 +1,66 @@ +/* + * This file is part of ViaForge - https://github.com/FlorianMichael/ViaForge + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viaforge.mixin; + +import com.viaversion.viaversion.util.Pair; +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; +import de.florianmichael.viaforge.gui.GuiProtocolSelector; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.EditServerScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.network.chat.Component; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EditServerScreen.class) +public class MixinEditServerScreen extends Screen { + + @Shadow + @Final + private ServerData serverData; + + public MixinEditServerScreen(Component title) { + super(title); + } + + @Inject(method = "init", at = @At("RETURN")) + public void initGui(CallbackInfo ci) { + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + + if (config.isShowAddServerButton()) { + final Pair pos = config.getAddServerScreenButtonPosition().getPosition(this.width, this.height); + + final VersionEnum target = ((ExtendedServerData) serverData).viaforge_getVersion(); + addRenderableWidget(Button.builder(Component.literal(target != null ? target.getName() : "Set Version"), b -> { + minecraft.setScreen(new GuiProtocolSelector(this, true, (version, parent) -> { + // Set version and go back to the parent screen. + ((ExtendedServerData) serverData).viaforge_setVersion(version); + minecraft.setScreen(parent); + })); + }).bounds(pos.key(), pos.value(), 100, 20).build()); + } + } +} diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java index c1bfb20..eba26e1 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinJoinMultiplayerScreen.java @@ -17,8 +17,9 @@ */ package de.florianmichael.viaforge.mixin; -import de.florianmichael.viaforge.ViaForge120; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -32,14 +33,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(JoinMultiplayerScreen.class) public class MixinJoinMultiplayerScreen extends Screen { - protected MixinJoinMultiplayerScreen(Component p_96550_) { - super(p_96550_); + public MixinJoinMultiplayerScreen(Component title) { + super(title); } @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { - if (ViaForgeCommon.getManager().getConfig().isShowMultiplayerButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java new file mode 100644 index 0000000..6061579 --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerData.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.nbt.CompoundTag; +import net.raphimc.vialoader.util.VersionEnum; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerData.class) +public class MixinServerData implements ExtendedServerData { + + @Unique + private VersionEnum viaforge_version; + + @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + public void saveVersion(CallbackInfoReturnable cir, CompoundTag compoundtag) { + if (viaforge_version != null) { + compoundtag.putInt("viaforge_version", viaforge_version.getVersion()); + } + } + + @Inject(method = "read", at = @At(value = "TAIL")) + private static void getVersion(CompoundTag compoundnbt, CallbackInfoReturnable cir) { + if (compoundnbt.contains("viaforge_version")) { + ((ExtendedServerData) cir.getReturnValue()).viaforge_setVersion(VersionEnum.fromProtocolId(compoundnbt.getInt("viaforge_version"))); + } + } + + @Inject(method = "copyFrom", at = @At("HEAD")) + public void track(ServerData serverDataIn, CallbackInfo ci) { + if (serverDataIn instanceof ExtendedServerData) { + viaforge_version = ((ExtendedServerData) serverDataIn).viaforge_getVersion(); + } + } + + @Override + public VersionEnum viaforge_getVersion() { + return viaforge_version; + } + + @Override + public void viaforge_setVersion(VersionEnum version) { + viaforge_version = version; + } +} diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java new file mode 100644 index 0000000..11d2d64 --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinServerStatusPinger.java @@ -0,0 +1,51 @@ +package de.florianmichael.viaforge.mixin; + +import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.gui.ExtendedServerData; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.multiplayer.ServerStatusPinger; +import net.minecraft.network.Connection; +import net.minecraft.util.SampleLogger; +import net.raphimc.vialoader.util.VersionEnum; +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 java.net.InetSocketAddress; + +@Mixin(ServerStatusPinger.class) +public class MixinServerStatusPinger { + + @Unique + private ServerData viaforge_serverData; + + @Inject(method = "pingServer", at = @At("HEAD")) + public void trackServerData(ServerData server, Runnable p_147224_2_, CallbackInfo ci) { + viaforge_serverData = server; + } + + @Redirect(method = "pingServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/Connection;connectToServer(Ljava/net/InetSocketAddress;ZLnet/minecraft/util/SampleLogger;)Lnet/minecraft/network/Connection;")) + public Connection trackVersion(InetSocketAddress address, boolean b, SampleLogger sampleLogger) { + // We need to track the version of the server we are connecting to, so we can later + // use it to determine the protocol version to use. + // We hope that the current server data is not null + + if (viaforge_serverData instanceof ExtendedServerData) { + final VersionEnum version = ((ExtendedServerData) viaforge_serverData).viaforge_getVersion(); + if (version != null) { + ViaForgeCommon.getManager().setTargetVersionSilent(version); + } else { + // If the server data does not contain a version, we need to restore the version + // we had before, so we don't use the wrong version. + ViaForgeCommon.getManager().restoreVersion(); + } + + viaforge_serverData = null; + } + + return Connection.connectToServer(address, b, sampleLogger); + } +} diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java index 36fd064..740dbdc 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/MixinTitleScreen.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viaforge.mixin; +import com.viaversion.viaversion.util.Pair; import de.florianmichael.viaforge.ViaForge120; import de.florianmichael.viaforge.common.ViaForgeCommon; +import de.florianmichael.viaforge.common.platform.ViaForgeConfig; import de.florianmichael.viaforge.gui.GuiProtocolSelector; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -32,16 +34,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(TitleScreen.class) public class MixinTitleScreen extends Screen { - protected MixinTitleScreen(Component p_96550_) { - super(p_96550_); + public MixinTitleScreen(Component title) { + super(title); } - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("RETURN")) public void hookViaForgeButton(CallbackInfo ci) { ViaForgeCommon.init(ViaForge120.PLATFORM); - if (ViaForgeCommon.getManager().getConfig().isShowMainMenuButton()) { - addRenderableWidget(new Button.Builder(Component.literal("ViaForge"), b -> minecraft.setScreen(new GuiProtocolSelector(this))).bounds(5, 6, 98, 20).build()); + final ViaForgeConfig config = ViaForgeCommon.getManager().getConfig(); + if (config.isShowMainMenuButton()) { + final Pair pos = config.getViaForgeButtonPosition().getPosition(this.width, this.height); + + addRenderableWidget(Button.builder(Component.literal("ViaForge"), buttons -> GuiProtocolSelector.open(minecraft)).bounds(pos.key(), pos.value(), 100, 20).build()); } } } diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java index 0c4566c..bea874d 100644 --- a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/mixin/fixes/MixinLocalPlayer.java @@ -34,4 +34,4 @@ public class MixinLocalPlayer extends AbstractClientPlayer { } return lastOnGround; } -} +} \ No newline at end of file diff --git a/viaforge-mc120/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java new file mode 100644 index 0000000..977a0b4 --- /dev/null +++ b/viaforge-mc120/src/main/java/de/florianmichael/viaforge/provider/ViaForgeGameProfileFetcher.java @@ -0,0 +1,56 @@ +package de.florianmichael.viaforge.provider; + +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; +import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; + +import java.net.Proxy; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class ViaForgeGameProfileFetcher extends GameProfileFetcher { + public final static HttpAuthenticationService AUTHENTICATION_SERVICE = new YggdrasilAuthenticationService(Proxy.NO_PROXY); + public final static MinecraftSessionService SESSION_SERVICE = AUTHENTICATION_SERVICE.createMinecraftSessionService(); + public final static GameProfileRepository GAME_PROFILE_REPOSITORY = AUTHENTICATION_SERVICE.createProfileRepository(); + + @Override + public UUID loadMojangUUID(String playerName) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + GAME_PROFILE_REPOSITORY.findProfilesByNames(new String[]{playerName}, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(com.mojang.authlib.GameProfile profile) { + future.complete(profile); + } + + @Override + public void onProfileLookupFailed(String profileName, Exception exception) { + future.completeExceptionally(exception); + } + }); + if (!future.isDone()) { + future.completeExceptionally(new ProfileNotFoundException()); + } + return future.get().getId(); + } + + @Override + public GameProfile loadGameProfile(UUID uuid) throws Exception { + final var result = SESSION_SERVICE.fetchProfile(uuid, true); + if (result == null) throw new ProfileNotFoundException(); + + final var profile = result.profile(); + final var gameProfile = new GameProfile(profile.getName(), profile.getId()); + + for (final var entry : profile.getProperties().entries()) { + final Property prop = entry.getValue(); + gameProfile.addProperty(new GameProfile.Property(prop.name(), prop.value(), prop.signature())); + } + return gameProfile; + } +} diff --git a/viaforge-mc120/src/main/resources/mixins.viaforge-mc120.json b/viaforge-mc120/src/main/resources/mixins.viaforge-mc120.json index 416093a..71be943 100644 --- a/viaforge-mc120/src/main/resources/mixins.viaforge-mc120.json +++ b/viaforge-mc120/src/main/resources/mixins.viaforge-mc120.json @@ -4,10 +4,15 @@ "compatibilityLevel": "JAVA_8", "package": "de.florianmichael.viaforge.mixin", "client": [ + "MixinClientHandshakePacketListenerImpl", "MixinConnection", "MixinConnection_1", + "MixinDebugScreenOverlay", "MixinDirectJoinServerScreen", + "MixinEditServerScreen", "MixinJoinMultiplayerScreen", + "MixinServerData", + "MixinServerStatusPinger", "MixinTitleScreen", "fixes.MixinLocalPlayer" ],