diff --git a/gradle.properties b/gradle.properties index 5df8251..ce80272 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ loader_version=0.15.10 viaver_version=4.10.0-24w09a-SNAPSHOT yaml_version=2.2 -publish_mc_versions=1.20.5, 1.20.4, 1.19.4, 1.18.2, 1.17.1, 1.16.5, 1.15.2, 1.14.4, 1.12.2, 1.8.9 +publish_mc_versions=1.20.5, 1.20.4, 1.20.1, 1.19.4, 1.18.2, 1.17.1, 1.16.5, 1.15.2, 1.14.4, 1.12.2, 1.8.9 # example: 1.19.1-rc1. Can be a blank value modrinth_mc_snapshot= # example: 1.19-Snapshot. Can be a blank value diff --git a/settings.gradle.kts b/settings.gradle.kts index 8d53e5f..c8daab4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,6 +15,7 @@ include("viafabric-mc1165") include("viafabric-mc1171") include("viafabric-mc1182") include("viafabric-mc1194") +include("viafabric-mc1201") include("viafabric-mc1204") include("viafabric-mc1205") diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b59a7aa..b1cf0df 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ }, "depends": { "fabricloader": ">=0.14.0", - "minecraft": ["1.8.9", "1.12.2", "1.14.4", "1.15.2", "1.16.5", "1.17.1", "1.18.2", "1.19.4", ">=1.20.3"], + "minecraft": ["1.8.9", "1.12.2", "1.14.4", "1.15.2", "1.16.5", "1.17.1", "1.18.2", "1.19.4", "1.20.1", "1.20.3", "1.20.4", "1.20.5"], "viaversion": ">=4.9.3" }, "breaks": { diff --git a/viafabric-mc1201/build.gradle.kts b/viafabric-mc1201/build.gradle.kts new file mode 100644 index 0000000..24a891c --- /dev/null +++ b/viafabric-mc1201/build.gradle.kts @@ -0,0 +1,11 @@ +dependencies { + minecraft("com.mojang:minecraft:1.20.1") + mappings("net.fabricmc:yarn:1.20.1+build.2:v2") + + modImplementation("net.fabricmc.fabric-api:fabric-api:0.83.1+1.20.1") + modImplementation("com.terraformersmc:modmenu:7.1.0") +} + +tasks.compileJava { + options.release.set(17) +} \ No newline at end of file diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabric.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabric.java new file mode 100644 index 0000000..e004720 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabric.java @@ -0,0 +1,119 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.viaversion.fabric.common.config.VFConfig; +import com.viaversion.fabric.common.platform.FabricInjector; +import com.viaversion.fabric.common.protocol.HostnameParserProtocol; +import com.viaversion.fabric.common.util.JLoggerToLog4j; +import com.viaversion.fabric.mc1201.commands.VFCommandHandler; +import com.viaversion.fabric.mc1201.platform.FabricPlatform; +import com.viaversion.fabric.mc1201.platform.VFLoader; +import com.viaversion.viaversion.ViaManagerImpl; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import io.netty.channel.DefaultEventLoop; +import io.netty.channel.EventLoop; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.command.CommandSource; +import org.apache.logging.log4j.LogManager; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Logger; + +public class ViaFabric implements ModInitializer { + public static final Logger JLOGGER = new JLoggerToLog4j(LogManager.getLogger("ViaFabric")); + public static final ExecutorService ASYNC_EXECUTOR; + public static final EventLoop EVENT_LOOP; + public static final CompletableFuture INIT_FUTURE = new CompletableFuture<>(); + public static VFConfig config; + + static { + ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("ViaFabric-%d").build(); + ASYNC_EXECUTOR = Executors.newFixedThreadPool(8, factory); + EVENT_LOOP = new DefaultEventLoop(factory); + EVENT_LOOP.submit(INIT_FUTURE::join); // https://github.com/ViaVersion/ViaFabric/issues/53 ugly workaround code but works tm + } + + public static LiteralArgumentBuilder command(String commandName) { + return LiteralArgumentBuilder.literal(commandName) + .then( + RequiredArgumentBuilder + .argument("args", StringArgumentType.greedyString()) + .executes(((VFCommandHandler) Via.getManager().getCommandHandler())::execute) + .suggests(((VFCommandHandler) Via.getManager().getCommandHandler())::suggestion) + ) + .executes(((VFCommandHandler) Via.getManager().getCommandHandler())::execute); + } + + @Override + public void onInitialize() { + FabricPlatform platform = new FabricPlatform(); + + Via.init(ViaManagerImpl.builder() + .injector(new FabricInjector()) + .loader(new VFLoader()) + .commandHandler(new VFCommandHandler()) + .platform(platform).build()); + + platform.init(); + + ViaManagerImpl manager = (ViaManagerImpl) Via.getManager(); + manager.init(); + + HostnameParserProtocol.INSTANCE.initialize(); + HostnameParserProtocol.INSTANCE.register(Via.getManager().getProviders()); + ProtocolVersion.register(-2, "AUTO"); + + FabricLoader.getInstance().getEntrypoints("viafabric:via_api_initialized", Runnable.class).forEach(Runnable::run); + + registerCommandsV1(); + + config = new VFConfig(FabricLoader.getInstance().getConfigDir().resolve("ViaFabric") + .resolve("viafabric.yml").toFile()); + + manager.onServerLoaded(); + + INIT_FUTURE.complete(null); + } + + + private void registerCommandsV1() { + try { + CommandRegistrationCallback.EVENT.register((dispatcher, dedicated, env) -> dispatcher.register(command("viaversion"))); + CommandRegistrationCallback.EVENT.register((dispatcher, dedicated, env) -> dispatcher.register(command("viaver"))); + CommandRegistrationCallback.EVENT.register((dispatcher, dedicated, env) -> dispatcher.register(command("vvfabric"))); + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(command("viafabricclient"))); + } + } catch (NoClassDefFoundError ignored) { + JLOGGER.info("Couldn't register command as Fabric Commands V1 isn't installed"); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabricClient.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabricClient.java new file mode 100644 index 0000000..632c7e7 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/ViaFabricClient.java @@ -0,0 +1,56 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201; + +import com.viaversion.fabric.mc1201.gui.ViaConfigScreen; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.Screens; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TexturedButtonWidget; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class ViaFabricClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + registerGui(); + } + + private void registerGui() { + try { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (!(screen instanceof MultiplayerScreen)) return; + ButtonWidget enableClientSideViaVersion = new TexturedButtonWidget(scaledWidth / 2 + 113, 10, + 40, 20, // Size + 0, 0, // Start pos of texture + 20, // v Hover offset + new Identifier("viafabric:textures/gui/widgets.png"), + 256, 256, // Texture size + it -> MinecraftClient.getInstance().setScreen(new ViaConfigScreen(screen)), + Text.translatable("gui.via_button")); + if (ViaFabric.config.isHideButton()) enableClientSideViaVersion.visible = false; + Screens.getButtons(screen).add(enableClientSideViaVersion); + }); + } catch (NoClassDefFoundError ignored) { + ViaFabric.JLOGGER.info("Couldn't register screen handler as Fabric Screen isn't installed"); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/NMSCommandSender.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/NMSCommandSender.java new file mode 100644 index 0000000..7b883db --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/NMSCommandSender.java @@ -0,0 +1,78 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.commands; + +import com.viaversion.viaversion.util.ComponentUtil; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandSource; +import net.minecraft.entity.Entity; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import com.viaversion.viaversion.api.command.ViaCommandSender; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +public class NMSCommandSender implements ViaCommandSender { + private final CommandSource source; + + public NMSCommandSender(CommandSource source) { + this.source = source; + } + + @Override + public boolean hasPermission(String s) { + // https://gaming.stackexchange.com/questions/138602/what-does-op-permission-level-do + return source.hasPermissionLevel(3); + } + + public static MutableText fromLegacy(String legacy) { + return Text.Serializer.fromJson(ComponentUtil.legacyToJsonString(legacy)); + } + + @Override + public void sendMessage(String s) { + if (source instanceof ServerCommandSource) { + ((ServerCommandSource) source).sendFeedback(() -> fromLegacy(s), false); + } else if (source instanceof FabricClientCommandSource) { + ((FabricClientCommandSource) source).sendFeedback(fromLegacy(s)); + } + } + + @Override + public UUID getUUID() { + if (source instanceof ServerCommandSource) { + Entity entity = ((ServerCommandSource) source).getEntity(); + if (entity != null) return entity.getUuid(); + } else if (source instanceof FabricClientCommandSource) { + return ((FabricClientCommandSource) source).getPlayer().getUuid(); + } + return UUID.nameUUIDFromBytes(getName().getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String getName() { + if (source instanceof ServerCommandSource) { + return ((ServerCommandSource) source).getName(); + } else if (source instanceof FabricClientCommandSource) { + return ((FabricClientCommandSource) source).getPlayer().getEntityName(); + } + return "?"; + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/VFCommandHandler.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/VFCommandHandler.java new file mode 100644 index 0000000..3575157 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/commands/VFCommandHandler.java @@ -0,0 +1,72 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.commands; + +import com.viaversion.fabric.common.commands.subs.LeakDetectSubCommand; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.CommandSource; +import com.viaversion.viaversion.commands.ViaCommandHandler; + +import java.util.concurrent.CompletableFuture; + +public class VFCommandHandler extends ViaCommandHandler { + { + try { + registerSubCommand(new LeakDetectSubCommand()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public int execute(CommandContext ctx) { + String[] args = new String[0]; + try { + args = StringArgumentType.getString(ctx, "args").split(" "); + } catch (IllegalArgumentException ignored) { + } + onCommand( + new NMSCommandSender(ctx.getSource()), + args + ); + return 1; + } + + public CompletableFuture suggestion(CommandContext ctx, SuggestionsBuilder builder) { + String[] args; + try { + args = StringArgumentType.getString(ctx, "args").split(" ", -1); + } catch (IllegalArgumentException ignored) { + args = new String[]{""}; + } + String[] pref = args.clone(); + pref[pref.length - 1] = ""; + String prefix = String.join(" ", pref); + onTabComplete(new NMSCommandSender(ctx.getSource()), args) + .stream() + .map(it -> { + SuggestionsBuilder b = new SuggestionsBuilder(builder.getInput(), prefix.length() + builder.getStart()); + b.suggest(it); + return b; + }) + .forEach(builder::add); + return builder.buildFuture(); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ModMenuConfig.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ModMenuConfig.java new file mode 100644 index 0000000..9fdd2bd --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ModMenuConfig.java @@ -0,0 +1,36 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.gui; + +import com.google.common.collect.ImmutableMap; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; + +import java.util.Map; + +public class ModMenuConfig implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return ViaConfigScreen::new; + } + + @Override + public Map> getProvidedConfigScreenFactories() { + return ImmutableMap.of("viafabric", getModConfigScreenFactory()); + } +} \ No newline at end of file diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ViaConfigScreen.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ViaConfigScreen.java new file mode 100644 index 0000000..e6d92d3 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/gui/ViaConfigScreen.java @@ -0,0 +1,176 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.gui; + +import com.viaversion.fabric.common.config.AbstractViaConfigScreen; +import com.viaversion.fabric.mc1201.ViaFabric; +import com.viaversion.fabric.common.util.ProtocolUtils; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ConfirmScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; + +import java.util.concurrent.CompletableFuture; + +@Environment(EnvType.CLIENT) +public class ViaConfigScreen extends Screen implements AbstractViaConfigScreen { + private static CompletableFuture latestProtocolSave; + private final Screen parent; + private TextFieldWidget protocolVersion; + + public ViaConfigScreen(Screen parent) { + super(Text.translatable(TITLE_TRANSLATE_ID)); + this.parent = parent; + } + + @Override + protected void init() { + int entries = 0; + + this.addDrawableChild(ButtonWidget + .builder(getClientSideText(), this::onClickClientSide) + .dimensions(calculatePosX(this.width, entries), + calculatePosY(this.height, entries), 150, 20) + .build()); + entries++; + + this.addDrawableChild(ButtonWidget + .builder(getHideViaButtonText(), this::onHideViaButton) + .dimensions(calculatePosX(this.width, entries), + calculatePosY(this.height, entries), 150, 20) + .build()); + entries++; + + protocolVersion = new TextFieldWidget(this.textRenderer, + calculatePosX(this.width, entries), + calculatePosY(this.height, entries), + 150, 20, Text.translatable("gui.protocol_version_field.name")); + entries++; + + protocolVersion.setTextPredicate(ProtocolUtils::isStartOfProtocolText); + protocolVersion.setChangedListener(this::onChangeVersionField); + int clientSideVersion = ViaFabric.config.getClientSideVersion(); + protocolVersion.setText(ProtocolUtils.getProtocolName(clientSideVersion)); + + this.addDrawableChild(protocolVersion); + + this.addDrawableChild(ButtonWidget + .builder(ScreenTexts.DONE, (it) -> close()) + .dimensions(this.width / 2 - 100, this.height - 40, 200, 20) + .build()); + } + + private void onChangeVersionField(String text) { + protocolVersion.setSuggestion(null); + int newVersion = ViaFabric.config.getClientSideVersion(); + + Integer parsed = ProtocolUtils.parseProtocolId(text); + boolean validProtocol; + + if (parsed != null) { + newVersion = parsed; + validProtocol = true; + } else { + validProtocol = false; + String[] suggestions = ProtocolUtils.getProtocolSuggestions(text); + if (suggestions.length == 1) { + protocolVersion.setSuggestion(suggestions[0].substring(text.length())); + } + } + + protocolVersion.setEditableColor(getProtocolTextColor(ProtocolVersion.getProtocol(newVersion), validProtocol)); + + int finalNewVersion = newVersion; + if (latestProtocolSave == null) latestProtocolSave = CompletableFuture.completedFuture(null); + ViaFabric.config.setClientSideVersion(finalNewVersion); + latestProtocolSave = latestProtocolSave.thenRunAsync(ViaFabric.config::save, ViaFabric.ASYNC_EXECUTOR); + } + + private void onClickClientSide(ButtonWidget widget) { + if (!ViaFabric.config.isClientSideEnabled()) { + MinecraftClient.getInstance().setScreen(new ConfirmScreen( + answer -> { + if (answer) { + ViaFabric.config.setClientSideEnabled(true); + ViaFabric.config.setClientSideVersion(-2); // AUTO + ViaFabric.config.save(); + widget.setMessage(getClientSideText()); + } + MinecraftClient.getInstance().setScreen(this); + }, + Text.translatable("gui.enable_client_side.question"), + Text.translatable("gui.enable_client_side.warning"), + Text.translatable("gui.enable_client_side.enable"), + Text.translatable("gui.cancel") + )); + } else { + ViaFabric.config.setClientSideEnabled(false); + ViaFabric.config.save(); + } + widget.setMessage(getClientSideText()); + } + + @Override + public void removed() { + ViaFabric.config.save(); + } + + @Override + public void close() { + this.client.setScreen(this.parent); + } + + private Text getClientSideText() { + return ViaFabric.config.isClientSideEnabled() ? + Text.translatable("gui.client_side.disable") + : Text.translatable("gui.client_side.enable"); + } + + private Text getHideViaButtonText() { + return ViaFabric.config.isHideButton() ? + Text.translatable("gui.hide_via_button.disable") : Text.translatable("gui.hide_via_button.enable"); + } + + private void onHideViaButton(ButtonWidget widget) { + ViaFabric.config.setHideButton(!ViaFabric.config.isHideButton()); + ViaFabric.config.save(); + widget.setMessage(getHideViaButtonText()); + } + + @Override + public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) { + this.renderBackground(drawContext); + drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 20, 16777215); + super.render(drawContext, mouseX, mouseY, delta); + } + + @Override + public void tick() { + super.tick(); + protocolVersion.tick(); + } +} + diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/address/client/MixinAllowedAddressResolver.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/address/client/MixinAllowedAddressResolver.java new file mode 100644 index 0000000..c0ebe58 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/address/client/MixinAllowedAddressResolver.java @@ -0,0 +1,63 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.address.client; + +import com.viaversion.fabric.common.AddressParser; +import net.minecraft.client.network.Address; +import net.minecraft.client.network.AllowedAddressResolver; +import net.minecraft.client.network.ServerAddress; +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.CallbackInfoReturnable; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Optional; + +@Mixin(AllowedAddressResolver.class) +public abstract class MixinAllowedAddressResolver { + @Shadow + public abstract Optional
resolve(ServerAddress address); + + @Inject(method = "resolve", at = @At(value = "HEAD"), cancellable = true) + private void resolveVF(ServerAddress address, CallbackInfoReturnable> cir) { + AddressParser viaAddr = new AddressParser().parse(address.getAddress()); + if (viaAddr.viaSuffix == null) { + return; + } + + ServerAddress realAddress = new ServerAddress(viaAddr.serverAddress, address.getPort()); + + cir.setReturnValue(resolve(realAddress).map(it -> viaFabric$addSuffix(it, viaAddr.getSuffixWithOptions()))); + } + + @Unique + private Address viaFabric$addSuffix(Address it, String viaSuffix) { + try { + return Address.create(new InetSocketAddress( + InetAddress.getByAddress(it.getHostName() + "." + viaSuffix, + it.getInetSocketAddress().getAddress().getAddress()), it.getPort())); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnection.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnection.java new file mode 100644 index 0000000..7566047 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnection.java @@ -0,0 +1,41 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.debug.client; + +import io.netty.channel.ChannelHandlerContext; +import net.minecraft.network.ClientConnection; +import org.slf4j.Logger; +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(ClientConnection.class) +public abstract class MixinClientConnection { + + @Shadow + @Final + private static Logger LOGGER; + + @Inject(method = "exceptionCaught", at = @At("TAIL")) + public void exceptionCaught(ChannelHandlerContext context, Throwable ex, CallbackInfo ci) { + LOGGER.error("Packet error", ex); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnectionAccessor.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnectionAccessor.java new file mode 100644 index 0000000..d02d1ad --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinClientConnectionAccessor.java @@ -0,0 +1,29 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.debug.client; + +import io.netty.channel.Channel; +import net.minecraft.network.ClientConnection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ClientConnection.class) +public interface MixinClientConnectionAccessor { + @Accessor + Channel getChannel(); +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinDebugHud.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinDebugHud.java new file mode 100644 index 0000000..53d5a46 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/debug/client/MixinDebugHud.java @@ -0,0 +1,54 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.debug.client; + +import com.viaversion.fabric.common.handler.CommonTransformer; +import com.viaversion.fabric.common.handler.FabricDecodeHandler; +import com.viaversion.viaversion.api.connection.ProtocolInfo; +import io.netty.channel.ChannelHandler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.hud.DebugHud; +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 com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; + +import java.util.List; + +@Mixin(DebugHud.class) +public class MixinDebugHud { + @Inject(at = @At("RETURN"), method = "getLeftText") + protected void getLeftText(CallbackInfoReturnable> info) { + String line = "[ViaFabric] I: " + Via.getManager().getConnectionManager().getConnections().size() + " (F: " + + Via.getManager().getConnectionManager().getConnectedClients().size() + ")"; + @SuppressWarnings("ConstantConditions") ChannelHandler viaDecoder = ((MixinClientConnectionAccessor) MinecraftClient.getInstance().getNetworkHandler() + .getConnection()).getChannel().pipeline().get(CommonTransformer.HANDLER_DECODER_NAME); + if (viaDecoder instanceof FabricDecodeHandler) { + ProtocolInfo protocol = ((FabricDecodeHandler) viaDecoder).getInfo().getProtocolInfo(); + if (protocol != null) { + ProtocolVersion serverVer = ProtocolVersion.getProtocol(protocol.getServerProtocolVersion()); + ProtocolVersion clientVer = ProtocolVersion.getProtocol(protocol.getProtocolVersion()); + line += " / C: " + clientVer + " S: " + serverVer + " A: " + protocol.getUser().isActive(); + } + } + + info.getReturnValue().add(line); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinMultiplayerServerListPingerListener.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinMultiplayerServerListPingerListener.java new file mode 100644 index 0000000..659c61e --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinMultiplayerServerListPingerListener.java @@ -0,0 +1,50 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.gui.client; + +import com.viaversion.fabric.common.gui.ViaServerInfo; +import com.viaversion.fabric.common.handler.FabricDecodeHandler; +import com.viaversion.fabric.mc1201.mixin.debug.client.MixinClientConnectionAccessor; +import net.minecraft.client.network.ServerInfo; +import net.minecraft.network.ClientConnection; +import net.minecraft.network.listener.ClientQueryPacketListener; +import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +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.client.network.MultiplayerServerListPinger$1") +public abstract class MixinMultiplayerServerListPingerListener implements ClientQueryPacketListener { + @Accessor + abstract ClientConnection getField_3774(); // Synthetic + + @Accessor + abstract ServerInfo getField_3776(); // Synthetic + + @Inject(method = "onResponse(Lnet/minecraft/network/packet/s2c/query/QueryResponseS2CPacket;)V", at = @At(value = "HEAD")) + private void onResponseCaptureServerInfo(QueryResponseS2CPacket packet, CallbackInfo ci) { + FabricDecodeHandler decoder = ((MixinClientConnectionAccessor) this.getField_3774()).getChannel() + .pipeline().get(FabricDecodeHandler.class); + if (decoder != null) { + ((ViaServerInfo) getField_3776()).viaFabric$setTranslating(decoder.getInfo().isActive()); + ((ViaServerInfo) getField_3776()).viaFabric$setServerVer(decoder.getInfo().getProtocolInfo().getServerProtocolVersion()); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerEntry.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerEntry.java new file mode 100644 index 0000000..bfa94a7 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerEntry.java @@ -0,0 +1,65 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.gui.client; + +import com.viaversion.fabric.common.gui.ViaServerInfo; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; +import net.minecraft.client.network.ServerInfo; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.ArrayList; +import java.util.List; + +@Mixin(MultiplayerServerListWidget.ServerEntry.class) +public class MixinServerEntry { + @Shadow + @Final + private ServerInfo server; + + @Unique + private static final Identifier viaFabric$GUI_ICONS_TEXTURES = new Identifier("textures/gui/icons.png"); + + @Redirect(method = "render", at = @At(value = "INVOKE", ordinal = 0, + target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIFFIIII)V")) + private void redirectPingIcon(DrawContext instance, Identifier texture, int x, int y, float u, float v, int width, int height, int textureWidth, int textureHeight) { + if (texture.equals(viaFabric$GUI_ICONS_TEXTURES) && ((ViaServerInfo) this.server).viaFabric$translating()) { + instance.drawTexture(new Identifier("viafabric:textures/gui/icons.png"), x, y, u, v, width, height, textureWidth, textureHeight); + return; + } + instance.drawTexture(texture, x, y, u, v, width, height, textureWidth, textureHeight); + } + + @Redirect(method = "render", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/client/gui/screen/multiplayer/MultiplayerScreen;setMultiplayerScreenTooltip(Ljava/util/List;)V")) + private void addServerVer(MultiplayerScreen multiplayerScreen, List tooltipText) { + ProtocolVersion proto = ProtocolVersion.getProtocol(((ViaServerInfo) this.server).viaFabric$getServerVer()); + List lines = new ArrayList<>(tooltipText); + lines.add(Text.translatable("gui.ping_version.translated", proto.getName(), proto.getVersion())); + lines.add(this.server.version.copy()); + multiplayerScreen.setMultiplayerScreenTooltip(lines); + } +} \ No newline at end of file diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerInfo.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerInfo.java new file mode 100644 index 0000000..e00587c --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/gui/client/MixinServerInfo.java @@ -0,0 +1,52 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.gui.client; + +import com.viaversion.fabric.common.gui.ViaServerInfo; +import net.minecraft.client.network.ServerInfo; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(ServerInfo.class) +public class MixinServerInfo implements ViaServerInfo { + @Unique + private boolean viaFabric$translating; + + @Unique + private int viaFabric$serverVer; + + @Override + public int viaFabric$getServerVer() { + return viaFabric$serverVer; + } + + @Override + public void viaFabric$setServerVer(int ver) { + this.viaFabric$serverVer = ver; + } + + @Override + public boolean viaFabric$translating() { + return viaFabric$translating; + } + + @Override + public void viaFabric$setTranslating(boolean via) { + this.viaFabric$translating = via; + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinClientConnection.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinClientConnection.java new file mode 100644 index 0000000..ccd2248 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinClientConnection.java @@ -0,0 +1,39 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.pipeline; + +import com.viaversion.fabric.common.handler.PipelineReorderEvent; +import io.netty.channel.Channel; +import net.minecraft.network.ClientConnection; +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(ClientConnection.class) +public class MixinClientConnection { + @Shadow + private Channel channel; + + @Inject(method = "setCompressionThreshold", at = @At("RETURN")) + private void reorderCompression(int compressionThreshold, boolean rejectBad, CallbackInfo ci) { + channel.pipeline().fireUserEventTriggered(new PipelineReorderEvent()); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinServerNetworkIoChInit.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinServerNetworkIoChInit.java new file mode 100644 index 0000000..fc4c0fb --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/MixinServerNetworkIoChInit.java @@ -0,0 +1,45 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.pipeline; + +import com.viaversion.fabric.common.handler.CommonTransformer; +import com.viaversion.fabric.common.handler.FabricDecodeHandler; +import com.viaversion.fabric.common.handler.FabricEncodeHandler; +import com.viaversion.viaversion.connection.UserConnectionImpl; +import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; +import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.viaversion.viaversion.api.connection.UserConnection; + +@Mixin(targets = "net.minecraft.server.ServerNetworkIo$1") +public class MixinServerNetworkIoChInit { + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) + private void onInitChannel(Channel channel, CallbackInfo ci) { + if (channel instanceof SocketChannel) { + UserConnection user = new UserConnectionImpl(channel); + new ProtocolPipelineImpl(user); + + channel.pipeline().addBefore("encoder", CommonTransformer.HANDLER_ENCODER_NAME, new FabricEncodeHandler(user)); + channel.pipeline().addBefore("decoder", CommonTransformer.HANDLER_DECODER_NAME, new FabricDecodeHandler(user)); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnection.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnection.java new file mode 100644 index 0000000..b55cf24 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnection.java @@ -0,0 +1,43 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.pipeline.client; + +import com.viaversion.fabric.mc1201.ViaFabric; +import com.viaversion.fabric.mc1201.service.ProtocolAutoDetector; +import net.minecraft.network.ClientConnection; +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.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +@Mixin(ClientConnection.class) +public class MixinClientConnection { + @Inject(method = "connect", at = @At("HEAD")) + private static void onConnect(InetSocketAddress address, boolean useEpoll, CallbackInfoReturnable cir) { + try { + if (!ViaFabric.config.isClientSideEnabled()) return; + ProtocolAutoDetector.detectVersion(address).get(10, TimeUnit.SECONDS); + } catch (Exception e) { + ViaFabric.JLOGGER.log(Level.WARNING, "Could not auto-detect protocol for " + address + " " + e); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnectionChInit.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnectionChInit.java new file mode 100644 index 0000000..39c7cb6 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/mixin/pipeline/client/MixinClientConnectionChInit.java @@ -0,0 +1,47 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.mixin.pipeline.client; + +import com.viaversion.fabric.common.handler.FabricDecodeHandler; +import com.viaversion.fabric.common.handler.FabricEncodeHandler; +import com.viaversion.fabric.common.handler.CommonTransformer; +import com.viaversion.fabric.common.protocol.HostnameParserProtocol; +import com.viaversion.viaversion.connection.UserConnectionImpl; +import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; +import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.viaversion.viaversion.api.connection.UserConnection; + +@Mixin(targets = "net.minecraft.network.ClientConnection$1") +public class MixinClientConnectionChInit { + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) + private void onInitChannel(Channel channel, CallbackInfo ci) { + if (channel instanceof SocketChannel) { + UserConnection user = new UserConnectionImpl(channel, true); + new ProtocolPipelineImpl(user).add(HostnameParserProtocol.INSTANCE); + + channel.pipeline() + .addBefore("encoder", CommonTransformer.HANDLER_ENCODER_NAME, new FabricEncodeHandler(user)) + .addBefore("decoder", CommonTransformer.HANDLER_DECODER_NAME, new FabricDecodeHandler(user)); + } + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricNativeVersionProvider.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricNativeVersionProvider.java new file mode 100644 index 0000000..b87f3f1 --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricNativeVersionProvider.java @@ -0,0 +1,28 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.platform; + +import com.viaversion.fabric.common.platform.NativeVersionProvider; +import net.minecraft.SharedConstants; + +public class FabricNativeVersionProvider implements NativeVersionProvider { + @Override + public int getNativeServerVersion() { + return SharedConstants.getGameVersion().getProtocolVersion(); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricPlatform.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricPlatform.java new file mode 100644 index 0000000..87031ea --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/FabricPlatform.java @@ -0,0 +1,140 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.platform; + +import com.viaversion.fabric.common.commands.UserCommandSender; +import com.viaversion.fabric.common.platform.NativeVersionProvider; +import com.viaversion.fabric.common.provider.AbstractFabricPlatform; +import com.viaversion.fabric.common.util.FutureTaskId; +import com.viaversion.fabric.mc1201.ViaFabric; +import com.viaversion.fabric.mc1201.commands.NMSCommandSender; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.command.ViaCommandSender; +import io.netty.channel.EventLoop; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; +import java.util.logging.Level; + +public class FabricPlatform extends AbstractFabricPlatform { + public static MinecraftServer getServer() { + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + return getIntegratedServer(); + } + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + @Environment(EnvType.CLIENT) + private static MinecraftServer getIntegratedServer() { + return MinecraftClient.getInstance().getServer(); + } + + @Override + public FutureTaskId runSync(Runnable runnable) { + if (getServer() != null) { + return runServerSync(runnable); + } else { + return runEventLoop(runnable); + } + } + + private FutureTaskId runServerSync(Runnable runnable) { + // Kick task needs to be on main thread, it does already have error logger + return new FutureTaskId(CompletableFuture.runAsync(runnable, getServer())); + } + + @Override + public ViaCommandSender[] getOnlinePlayers() { + MinecraftServer server = getServer(); + if (server != null && server.isOnThread()) { + return getServerPlayers(); + } + return Via.getManager().getConnectionManager().getConnectedClients().values().stream() + .map(UserCommandSender::new) + .toArray(ViaCommandSender[]::new); + } + + private ViaCommandSender[] getServerPlayers() { + return getServer().getPlayerManager().getPlayerList().stream() + .map(Entity::getCommandSource) + .map(NMSCommandSender::new) + .toArray(ViaCommandSender[]::new); + } + + @Override + public void sendMessage(UUID uuid, String s) { + sendMessageServer(uuid, s); + } + + private void sendMessageServer(UUID uuid, String s) { + MinecraftServer server = getServer(); + if (server == null) return; + runServerSync(() -> { + ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); + if (player == null) return; + player.sendMessage(NMSCommandSender.fromLegacy(s), false); + }); + } + + @Override + public boolean kickPlayer(UUID uuid, String s) { + return kickServer(uuid, s); + } + + private boolean kickServer(UUID uuid, String s) { + MinecraftServer server = getServer(); + if (server == null) return false; + Supplier kickTask = () -> { + ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); + if (player == null) return false; + player.networkHandler.disconnect(NMSCommandSender.fromLegacy(s)); + return true; + }; + if (server.isOnThread()) { + return kickTask.get(); + } else { + ViaFabric.JLOGGER.log(Level.WARNING, "Weird!? Player kicking was called off-thread", new Throwable()); + runServerSync(kickTask::get); + } + return false; // Can't know if it worked + } + + @Override + protected void installNativeVersionProvider() { + Via.getManager().getProviders().use(NativeVersionProvider.class, new FabricNativeVersionProvider()); + } + + @Override + protected ExecutorService asyncService() { + return ViaFabric.ASYNC_EXECUTOR; + } + + @Override + protected EventLoop eventLoop() { + return ViaFabric.EVENT_LOOP; + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/VFLoader.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/VFLoader.java new file mode 100644 index 0000000..052eddf --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/platform/VFLoader.java @@ -0,0 +1,54 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.platform; + +import com.viaversion.fabric.mc1201.providers.VFHandItemProvider; +import com.viaversion.fabric.mc1201.providers.FabricVersionProvider; +import com.viaversion.fabric.mc1201.providers.VFPlayerAbilitiesProvider; +import com.viaversion.fabric.mc1201.providers.VFPlayerLookTargetProvider; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.PlayerLookTargetProvider; +import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.provider.PlayerAbilitiesProvider; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.platform.ViaPlatformLoader; +import com.viaversion.viaversion.api.protocol.version.VersionProvider; +import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider; + +public class VFLoader implements ViaPlatformLoader { + @Override + public void load() { + Via.getManager().getProviders().use(VersionProvider.class, new FabricVersionProvider()); + + if (Via.getPlatform().getConf().isItemCache()) { + VFHandItemProvider handProvider = new VFHandItemProvider(); + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + handProvider.registerClientTick(); + } + Via.getManager().getProviders().use(HandItemProvider.class, handProvider); + } + + Via.getManager().getProviders().use(PlayerAbilitiesProvider.class, new VFPlayerAbilitiesProvider()); + Via.getManager().getProviders().use(PlayerLookTargetProvider.class, new VFPlayerLookTargetProvider()); + } + + @Override + public void unload() { + // Nothing to do + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/FabricVersionProvider.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/FabricVersionProvider.java new file mode 100644 index 0000000..ca7b79f --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/FabricVersionProvider.java @@ -0,0 +1,52 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.providers; + +import com.viaversion.fabric.common.config.VFConfig; +import com.viaversion.fabric.common.provider.AbstractFabricVersionProvider; +import com.viaversion.fabric.mc1201.ViaFabric; +import com.viaversion.fabric.mc1201.service.ProtocolAutoDetector; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import io.netty.channel.ChannelPipeline; +import net.minecraft.network.ClientConnection; + +import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +public class FabricVersionProvider extends AbstractFabricVersionProvider { + @Override + protected Logger getLogger() { + return ViaFabric.JLOGGER; + } + + @Override + protected VFConfig getConfig() { + return ViaFabric.config; + } + + @Override + protected CompletableFuture detectVersion(InetSocketAddress address) { + return ProtocolAutoDetector.detectVersion(address); + } + + @Override + protected boolean isMulticonnectHandler(ChannelPipeline pipe) { + return pipe.get(ClientConnection.class).getPacketListener().getClass().getName().startsWith("net.earthcomputer.multiconnect"); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFHandItemProvider.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFHandItemProvider.java new file mode 100644 index 0000000..d5d36bc --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFHandItemProvider.java @@ -0,0 +1,74 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.providers; + +import com.viaversion.fabric.common.util.RemappingUtil; +import com.viaversion.fabric.mc1201.ViaFabric; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.minecraft.item.DataItem; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; + +public class VFHandItemProvider extends HandItemProvider { + public Item clientItem = null; + + @Override + public Item getHandItem(UserConnection info) { + if (info.isClientSide()) { + return getClientItem(); + } + return super.getHandItem(info); + } + + private Item getClientItem() { + if (clientItem == null) { + return new DataItem(0, (byte) 0, (short) 0, null); + } + return new DataItem(clientItem); + } + + @Environment(EnvType.CLIENT) + public void registerClientTick() { + try { + ClientTickEvents.END_WORLD_TICK.register(clientWorld -> tickClient()); + } catch (NoClassDefFoundError ignored) { + ViaFabric.JLOGGER.info("Fabric Lifecycle V1 isn't installed"); + } + } + + private void tickClient() { + ClientPlayerEntity p = MinecraftClient.getInstance().player; + if (p != null) { + clientItem = fromNative(p.getInventory().getMainHandStack()); + } + } + + private Item fromNative(ItemStack original) { + Identifier iid = Registries.ITEM.getId(original.getItem()); + int id = RemappingUtil.swordId(iid.toString()); + return new DataItem(id, (byte) original.getCount(), (short) original.getDamage(), null); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerAbilitiesProvider.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerAbilitiesProvider.java new file mode 100644 index 0000000..9ee08ee --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerAbilitiesProvider.java @@ -0,0 +1,39 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.providers; + +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.provider.PlayerAbilitiesProvider; +import net.minecraft.client.MinecraftClient; + +public class VFPlayerAbilitiesProvider extends PlayerAbilitiesProvider { + + @Override + public float getFlyingSpeed(UserConnection connection) { + if (!connection.isClientSide()) return super.getFlyingSpeed(connection); + + return MinecraftClient.getInstance().player.getAbilities().getFlySpeed(); + } + + @Override + public float getWalkingSpeed(UserConnection connection) { + if (!connection.isClientSide()) return super.getWalkingSpeed(connection); + + return MinecraftClient.getInstance().player.getAbilities().getWalkSpeed(); + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerLookTargetProvider.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerLookTargetProvider.java new file mode 100644 index 0000000..9efd56b --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/providers/VFPlayerLookTargetProvider.java @@ -0,0 +1,39 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.providers; + +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.minecraft.Position; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.PlayerLookTargetProvider; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; + +public class VFPlayerLookTargetProvider extends PlayerLookTargetProvider { + + @Override + public Position getPlayerLookTarget(UserConnection info) { + if (!info.isClientSide()) return null; + + if (MinecraftClient.getInstance().crosshairTarget instanceof BlockHitResult blockHitResult) { + final BlockPos pos = blockHitResult.getBlockPos(); + return new Position(pos.getX(), pos.getY(), pos.getZ()); + } + return null; + } +} diff --git a/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/service/ProtocolAutoDetector.java b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/service/ProtocolAutoDetector.java new file mode 100644 index 0000000..57edbee --- /dev/null +++ b/viafabric-mc1201/src/main/java/com/viaversion/fabric/mc1201/service/ProtocolAutoDetector.java @@ -0,0 +1,141 @@ +/* + * This file is part of ViaFabric - https://github.com/ViaVersion/ViaFabric + * Copyright (C) 2018-2024 ViaVersion 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 com.viaversion.fabric.mc1201.service; + +import com.viaversion.fabric.common.AddressParser; +import com.viaversion.fabric.mc1201.ViaFabric; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.timeout.ReadTimeoutHandler; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.network.*; +import net.minecraft.network.listener.ClientQueryPacketListener; +import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket; +import net.minecraft.network.packet.c2s.query.QueryRequestC2SPacket; +import net.minecraft.network.packet.s2c.query.QueryPongS2CPacket; +import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket; +import net.minecraft.server.ServerMetadata; +import net.minecraft.text.Text; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +@Environment(EnvType.CLIENT) +public class ProtocolAutoDetector { + private static final LoadingCache> SERVER_VER = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.SECONDS) + .build(CacheLoader.from((address) -> { + CompletableFuture future = new CompletableFuture<>(); + + try { + final ClientConnection clientConnection = new ClientConnection(NetworkSide.CLIENTBOUND); + + ChannelFuture ch = new Bootstrap() + .group(ClientConnection.CLIENT_IO_GROUP.get()) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + protected void initChannel(Channel channel) { + try { + channel.config().setOption(ChannelOption.TCP_NODELAY, true); + channel.config().setOption(ChannelOption.IP_TOS, 0x18); // Stolen from Velocity, low delay, high reliability + } catch (ChannelException ignored) { + } + + channel.pipeline() + .addLast("timeout", new ReadTimeoutHandler(30)) + .addLast("splitter", new SplitterHandler()) + .addLast("decoder", new DecoderHandler(NetworkSide.CLIENTBOUND)) + .addLast("prepender", new SizePrepender()) + .addLast("encoder", new PacketEncoder(NetworkSide.SERVERBOUND)) + .addLast("packet_handler", clientConnection); + } + }) + .connect(address); + + ch.addListener(future1 -> { + if (!future1.isSuccess()) { + future.completeExceptionally(future1.cause()); + } else { + ch.channel().eventLoop().execute(() -> { // needs to execute after channel init + clientConnection.setPacketListener(new ClientQueryPacketListener() { + @Override + public void onResponse(QueryResponseS2CPacket packet) { + ServerMetadata meta = packet.metadata(); + if (meta != null && meta.version().isPresent()) { + ProtocolVersion ver = ProtocolVersion.getProtocol(meta.version().get() + .protocolVersion()); + future.complete(ver); + ViaFabric.JLOGGER.info("Auto-detected " + ver + " for " + address); + } else { + future.completeExceptionally(new IllegalArgumentException("Null version in query response")); + } + clientConnection.disconnect(Text.empty()); + } + + @Override + public void onPong(QueryPongS2CPacket packet) { + clientConnection.disconnect(Text.literal("Pong not requested!")); + } + + @Override + public void onDisconnected(Text reason) { + future.completeExceptionally(new IllegalStateException(reason.getString())); + } + + @Override + public boolean isConnectionOpen() { + return ch.channel().isOpen(); + } + }); + + clientConnection.send(new HandshakeC2SPacket(address.getHostString(), + address.getPort(), NetworkState.STATUS)); + clientConnection.send(new QueryRequestC2SPacket()); + }); + } + }); + } catch (Throwable throwable) { + future.completeExceptionally(throwable); + } + + return future; + })); + + public static CompletableFuture detectVersion(InetSocketAddress address) { + try { + InetSocketAddress real = new InetSocketAddress(InetAddress.getByAddress + (new AddressParser().parse(address.getHostString()).serverAddress, + address.getAddress().getAddress()), address.getPort()); + return SERVER_VER.get(real); + } catch (UnknownHostException | ExecutionException e) { + ViaFabric.JLOGGER.log(Level.WARNING, "Protocol auto detector error: ", e); + return CompletableFuture.completedFuture(null); + } + } +} diff --git a/viafabric-mc1201/src/main/resources/fabric.mod.json b/viafabric-mc1201/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..3402ec3 --- /dev/null +++ b/viafabric-mc1201/src/main/resources/fabric.mod.json @@ -0,0 +1,78 @@ +{ + "schemaVersion": 1, + "id": "viafabric-mc1201", + "name": "ViaFabric for 1.20.1", + "version": "${version}", + "description": "${description}", + "license": "GPL-3.0", + "contact": { + "homepage": "https://viaversion.com/fabric", + "issues": "https://github.com/ViaVersion/ViaFabric/issues", + "sources": "https://github.com/ViaVersion/ViaFabric" + }, + "environment": "*", + "authors": [ + { + "name": "creeper123123321", + "contact": { + "homepage": "https://creeper123123321.github.io/" + } + }, + { + "name": "FlorianMichael/EnZaXD", + "contact": { + "homepage": "https://github.com/FlorianMichael" + } + }, + { + "name": "kennytv", + "contact": { + "homepage": "https://kennytv.eu" + } + }, + { + "name": "Kichura", + "contact": { + "homepage": "https://github.com/Kichura" + } + } + ], + "contributors": [ + { + "name": "GitHub contributors", + "contact": { + "homepage": "https://github.com/ViaVersion/ViaFabric/graphs/contributors" + } + } + ], + "entrypoints": { + "main": [ + "com.viaversion.fabric.mc1201.ViaFabric" + ], + "client": [ + "com.viaversion.fabric.mc1201.ViaFabricClient" + ], + "modmenu": [ + "com.viaversion.fabric.mc1201.gui.ModMenuConfig" + ] + }, + "depends": { + "fabric-resource-loader-v0": "*", + "minecraft": "1.20.1", + "viafabric": "*" + }, + "recommends": { + "fabric-command-api-v2": "*" + }, + "mixins": [ + "mixins.viafabric1201.address.json", + "mixins.viafabric1201.gui.json", + "mixins.viafabric1201.debug.json", + "mixins.viafabric1201.pipeline.json" + ], + "custom": { + "modmenu": { + "parent": "viafabric" + } + } +} diff --git a/viafabric-mc1201/src/main/resources/mixins.viafabric1201.address.json b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.address.json new file mode 100644 index 0000000..044e77b --- /dev/null +++ b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.address.json @@ -0,0 +1,13 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_17", + "package": "com.viaversion.fabric.mc1201.mixin.address", + "mixins": [ + ], + "client": [ + "client.MixinAllowedAddressResolver" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc1201/src/main/resources/mixins.viafabric1201.debug.json b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.debug.json new file mode 100644 index 0000000..aa9e88b --- /dev/null +++ b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.debug.json @@ -0,0 +1,15 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_17", + "package": "com.viaversion.fabric.mc1201.mixin.debug", + "mixins": [ + ], + "client": [ + "client.MixinClientConnectionAccessor", + "client.MixinDebugHud", + "client.MixinClientConnection" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc1201/src/main/resources/mixins.viafabric1201.gui.json b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.gui.json new file mode 100644 index 0000000..6c5cb07 --- /dev/null +++ b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.gui.json @@ -0,0 +1,15 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_17", + "package": "com.viaversion.fabric.mc1201.mixin.gui", + "mixins": [ + ], + "client": [ + "client.MixinMultiplayerServerListPingerListener", + "client.MixinServerEntry", + "client.MixinServerInfo" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc1201/src/main/resources/mixins.viafabric1201.pipeline.json b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.pipeline.json new file mode 100644 index 0000000..6bb592f --- /dev/null +++ b/viafabric-mc1201/src/main/resources/mixins.viafabric1201.pipeline.json @@ -0,0 +1,16 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_17", + "package": "com.viaversion.fabric.mc1201.mixin.pipeline", + "mixins": [ + "MixinClientConnection", + "MixinServerNetworkIoChInit" + ], + "client": [ + "client.MixinClientConnection", + "client.MixinClientConnectionChInit" + ], + "injectors": { + "defaultRequire": 1 + } +}