diff --git a/gradle.properties b/gradle.properties index f72c44b..4b7a41c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,12 +2,12 @@ # rip my ram org.gradle.jvmargs=-Xms32M -Xmx4G -XX:+UseG1GC -XX:+UseStringDeduplication -loader_version=0.14.14 -viaver_version=4.6.3-SNAPSHOT +loader_version=0.14.19 +viaver_version=4.7.0-23w13a-SNAPSHOT yaml_version=2.0 publish_mc_versions=1.19.4, 1.18.2, 1.17.1, 1.16.5, 1.15.2, 1.14.4, 1.8.9 # example: 1.19.1-rc2 -modrinth_mc_snapshot= +modrinth_mc_snapshot=23w13a # example: 1.19-Snapshot -curseforge_mc_snapshot= +curseforge_mc_snapshot=1.20-Snapshot diff --git a/settings.gradle.kts b/settings.gradle.kts index 894a84e..ff622ce 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,7 +14,7 @@ include("viafabric-mc116") include("viafabric-mc117") include("viafabric-mc118") include("viafabric-mc119") - +include("viafabric-mc120") plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 13052a9..26231d1 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ }, "depends": { "fabricloader": ">=0.10.0", - "minecraft": ["1.8.x", "1.14.4", "1.15.2", "~1.16.4", "1.17.1", "1.18.2", "~1.19.4-alpha.23.7.a"], + "minecraft": ["1.8.x", "1.14.4", "1.15.2", "~1.16.4", "1.17.1", "1.18.2", "~1.19.4", "1.20.x"], "viaversion": ">=4.0.0" }, "environment": "*", diff --git a/viafabric-mc120/build.gradle.kts b/viafabric-mc120/build.gradle.kts new file mode 100644 index 0000000..7c00bca --- /dev/null +++ b/viafabric-mc120/build.gradle.kts @@ -0,0 +1,11 @@ +dependencies { + minecraft("com.mojang:minecraft:23w13a") + mappings("net.fabricmc:yarn:23w13a+build.3:v2") + + modImplementation("net.fabricmc.fabric-api:fabric-api:0.76.2+1.20") + modImplementation("com.terraformersmc:modmenu:6.1.0-rc.4") +} + +tasks.compileJava { + options.release.set(17) +} diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabric.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabric.java new file mode 100644 index 0000000..6317ff6 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabric.java @@ -0,0 +1,102 @@ +package com.viaversion.fabric.mc120; + +import com.google.common.collect.Range; +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.mc120.commands.VRCommandHandler; +import com.viaversion.fabric.mc120.platform.FabricPlatform; +import com.viaversion.fabric.mc120.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(((VRCommandHandler) Via.getManager().getCommandHandler())::execute) + .suggests(((VRCommandHandler) Via.getManager().getCommandHandler())::suggestion) + ) + .executes(((VRCommandHandler) Via.getManager().getCommandHandler())::execute); + } + + @Override + public void onInitialize() { + FabricPlatform platform = new FabricPlatform(); + + Via.init(ViaManagerImpl.builder() + .injector(new FabricInjector()) + .loader(new VFLoader()) + .commandHandler(new VRCommandHandler()) + .platform(platform).build()); + + platform.init(); + + ViaManagerImpl manager = (ViaManagerImpl) Via.getManager(); + manager.init(); + + Via.getManager().getProtocolManager().registerBaseProtocol(HostnameParserProtocol.INSTANCE, Range.lessThan(Integer.MIN_VALUE)); + 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-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabricClient.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabricClient.java new file mode 100644 index 0000000..bbf0e9f --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/ViaFabricClient.java @@ -0,0 +1,39 @@ +package com.viaversion.fabric.mc120; + +import com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/commands/NMSCommandSender.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/commands/NMSCommandSender.java new file mode 100644 index 0000000..094443a --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/commands/NMSCommandSender.java @@ -0,0 +1,60 @@ +package com.viaversion.fabric.mc120.commands; + +import com.viaversion.fabric.common.util.RemappingUtil; +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.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(RemappingUtil.legacyToJson(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.fromString(getName()); + } + + @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-mc120/src/main/java/com/viaversion/fabric/mc120/commands/VRCommandHandler.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/commands/VRCommandHandler.java new file mode 100644 index 0000000..33dadcb --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/commands/VRCommandHandler.java @@ -0,0 +1,55 @@ +package com.viaversion.fabric.mc120.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 VRCommandHandler 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-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ModMenuConfig.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ModMenuConfig.java new file mode 100644 index 0000000..bcc6d0d --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ModMenuConfig.java @@ -0,0 +1,19 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ViaConfigScreen.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ViaConfigScreen.java new file mode 100644 index 0000000..0d03f96 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/gui/ViaConfigScreen.java @@ -0,0 +1,157 @@ +package com.viaversion.fabric.mc120.gui; + +import com.viaversion.fabric.common.config.AbstractViaConfigScreen; +import com.viaversion.fabric.mc120.ViaFabric; +import com.viaversion.fabric.common.util.ProtocolUtils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +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(newVersion, validProtocol)); + + int finalNewVersion = newVersion; + if (latestProtocolSave == null) latestProtocolSave = CompletableFuture.completedFuture(null); + ViaFabric.config.setClientSideVersion(finalNewVersion); + latestProtocolSave = latestProtocolSave.thenRunAsync(ViaFabric.config::saveConfig, 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.saveConfig(); + 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.saveConfig(); + } + widget.setMessage(getClientSideText()); + } + + @Override + public void removed() { + ViaFabric.config.saveConfig(); + } + + @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.saveConfig(); + widget.setMessage(getHideViaButtonText()); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); + drawCenteredTextWithShadow(matrices, this.textRenderer, this.title, this.width / 2, 20, 16777215); + super.render(matrices, mouseX, mouseY, delta); + } + + @Override + public void tick() { + super.tick(); + protocolVersion.tick(); + } +} + diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/address/client/MixinAllowedAddressResolver.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/address/client/MixinAllowedAddressResolver.java new file mode 100644 index 0000000..27cfbfd --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/address/client/MixinAllowedAddressResolver.java @@ -0,0 +1,44 @@ +package com.viaversion.fabric.mc120.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.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 -> viaFabricAddSuffix(it, viaAddr.getSuffixWithOptions()))); + } + + private Address viaFabricAddSuffix(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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinClientConnectionAccessor.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinClientConnectionAccessor.java new file mode 100644 index 0000000..5631862 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinClientConnectionAccessor.java @@ -0,0 +1,12 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinDebugHud.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinDebugHud.java new file mode 100644 index 0000000..576b22f --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/debug/client/MixinDebugHud.java @@ -0,0 +1,37 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinMultiplayerServerListPingerListener.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinMultiplayerServerListPingerListener.java new file mode 100644 index 0000000..96080aa --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinMultiplayerServerListPingerListener.java @@ -0,0 +1,33 @@ +package com.viaversion.fabric.mc120.mixin.gui.client; + +import com.viaversion.fabric.common.gui.ViaServerInfo; +import com.viaversion.fabric.common.handler.FabricDecodeHandler; +import com.viaversion.fabric.mc120.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()).setViaTranslating(decoder.getInfo().isActive()); + ((ViaServerInfo) getField_3776()).setViaServerVer(decoder.getInfo().getProtocolInfo().getServerProtocolVersion()); + } + } +} diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerEntry.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerEntry.java new file mode 100644 index 0000000..9eb66c1 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerEntry.java @@ -0,0 +1,44 @@ +package com.viaversion.fabric.mc120.mixin.gui.client; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.viaversion.fabric.common.gui.ViaServerInfo; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import net.minecraft.client.gui.DrawableHelper; +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.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; + + @Redirect(method = "render", at = @At(value = "INVOKE", ordinal = 0, + target = "Lcom/mojang/blaze3d/systems/RenderSystem;setShaderTexture(ILnet/minecraft/util/Identifier;)V")) + private void redirectPingIcon(int i, Identifier identifier) { + if (identifier.equals(DrawableHelper.GUI_ICONS_TEXTURE) && ((ViaServerInfo) this.server).isViaTranslating()) { + RenderSystem.setShaderTexture(i, new Identifier("viafabric:textures/gui/icons.png")); + return; + } + RenderSystem.setShaderTexture(i, identifier); + } + + @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).getViaServerVer()); + List lines = new ArrayList<>(tooltipText); + lines.add(Text.translatable("gui.ping_version.translated", proto.getName())); + multiplayerScreen.setMultiplayerScreenTooltip(lines); + } +} \ No newline at end of file diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerInfo.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerInfo.java new file mode 100644 index 0000000..8eb57ad --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/gui/client/MixinServerInfo.java @@ -0,0 +1,29 @@ +package com.viaversion.fabric.mc120.mixin.gui.client; + +import com.viaversion.fabric.common.gui.ViaServerInfo; +import net.minecraft.client.network.ServerInfo; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ServerInfo.class) +public class MixinServerInfo implements ViaServerInfo { + private boolean viaTranslating; + private int viaServerVer; + + public int getViaServerVer() { + return viaServerVer; + } + + public void setViaServerVer(int viaServerVer) { + this.viaServerVer = viaServerVer; + } + + @Override + public boolean isViaTranslating() { + return viaTranslating; + } + + @Override + public void setViaTranslating(boolean via) { + this.viaTranslating = via; + } +} diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinClientConnection.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinClientConnection.java new file mode 100644 index 0000000..e42ec38 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinClientConnection.java @@ -0,0 +1,22 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinServerNetworkIoChInit.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinServerNetworkIoChInit.java new file mode 100644 index 0000000..6cfd614 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/MixinServerNetworkIoChInit.java @@ -0,0 +1,28 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnection.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnection.java new file mode 100644 index 0000000..7a96c7d --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnection.java @@ -0,0 +1,26 @@ +package com.viaversion.fabric.mc120.mixin.pipeline.client; + +import com.viaversion.fabric.mc120.ViaFabric; +import com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnectionChInit.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnectionChInit.java new file mode 100644 index 0000000..b60113d --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/mixin/pipeline/client/MixinClientConnectionChInit.java @@ -0,0 +1,30 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricNativeVersionProvider.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricNativeVersionProvider.java new file mode 100644 index 0000000..7b6b7ee --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricNativeVersionProvider.java @@ -0,0 +1,11 @@ +package com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricPlatform.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricPlatform.java new file mode 100644 index 0000000..50afbfb --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/FabricPlatform.java @@ -0,0 +1,123 @@ +package com.viaversion.fabric.mc120.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.mc120.ViaFabric; +import com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/platform/VFLoader.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/VFLoader.java new file mode 100644 index 0000000..24eadce --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/platform/VFLoader.java @@ -0,0 +1,33 @@ +package com.viaversion.fabric.mc120.platform; + +import com.viaversion.fabric.mc120.providers.VRHandItemProvider; +import com.viaversion.fabric.mc120.providers.FabricVersionProvider; +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.bungee.providers.BungeeMovementTransmitter; +import com.viaversion.viaversion.api.protocol.version.VersionProvider; +import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider; +import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; + +public class VFLoader implements ViaPlatformLoader { + @Override + public void load() { + Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BungeeMovementTransmitter()); + Via.getManager().getProviders().use(VersionProvider.class, new FabricVersionProvider()); + + if (Via.getPlatform().getConf().isItemCache()) { + VRHandItemProvider handProvider = new VRHandItemProvider(); + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + handProvider.registerClientTick(); + } + Via.getManager().getProviders().use(HandItemProvider.class, handProvider); + } + } + + @Override + public void unload() { + // Nothing to do + } +} diff --git a/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/providers/FabricVersionProvider.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/providers/FabricVersionProvider.java new file mode 100644 index 0000000..31691b0 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/providers/FabricVersionProvider.java @@ -0,0 +1,35 @@ +package com.viaversion.fabric.mc120.providers; + +import com.viaversion.fabric.common.config.VFConfig; +import com.viaversion.fabric.common.provider.AbstractFabricVersionProvider; +import com.viaversion.fabric.mc120.ViaFabric; +import com.viaversion.fabric.mc120.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-mc120/src/main/java/com/viaversion/fabric/mc120/providers/VRHandItemProvider.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/providers/VRHandItemProvider.java new file mode 100644 index 0000000..89601ee --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/providers/VRHandItemProvider.java @@ -0,0 +1,57 @@ +package com.viaversion.fabric.mc120.providers; + +import com.viaversion.fabric.common.util.RemappingUtil; +import com.viaversion.fabric.mc120.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 VRHandItemProvider 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-mc120/src/main/java/com/viaversion/fabric/mc120/service/ProtocolAutoDetector.java b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/service/ProtocolAutoDetector.java new file mode 100644 index 0000000..3effc43 --- /dev/null +++ b/viafabric-mc120/src/main/java/com/viaversion/fabric/mc120/service/ProtocolAutoDetector.java @@ -0,0 +1,124 @@ +package com.viaversion.fabric.mc120.service; + +import com.viaversion.fabric.common.AddressParser; +import com.viaversion.fabric.mc120.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-mc120/src/main/resources/fabric.mod.json b/viafabric-mc120/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..0fa93a7 --- /dev/null +++ b/viafabric-mc120/src/main/resources/fabric.mod.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": 1, + "id": "viafabric-mc120", + "name": "ViaFabric for 1.20", + "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/" + } + } + ], + "contributors": [ + { + "name": "GitHub contributors", + "contact": { + "homepage": "https://github.com/ViaVersion/ViaFabric/graphs/contributors" + } + } + ], + "entrypoints": { + "main": [ + "com.viaversion.fabric.mc120.ViaFabric" + ], + "client": [ + "com.viaversion.fabric.mc120.ViaFabricClient" + ], + "modmenu": [ + "com.viaversion.fabric.mc120.gui.ModMenuConfig" + ] + }, + "depends": { + "fabric-resource-loader-v0": "*", + "minecraft": "1.20.x", + "viafabric": "*" + }, + "recommends": { + "fabric-command-api-v2": "*" + }, + "mixins": [ + "mixins.viafabric120.address.json", + "mixins.viafabric120.gui.json", + "mixins.viafabric120.debug.json", + "mixins.viafabric120.pipeline.json" + ], + "custom": { + "modmenu": { + "parent": "viafabric" + } + } +} diff --git a/viafabric-mc120/src/main/resources/mixins.viafabric120.address.json b/viafabric-mc120/src/main/resources/mixins.viafabric120.address.json new file mode 100644 index 0000000..be6f25d --- /dev/null +++ b/viafabric-mc120/src/main/resources/mixins.viafabric120.address.json @@ -0,0 +1,13 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_16", + "package": "com.viaversion.fabric.mc120.mixin.address", + "mixins": [ + ], + "client": [ + "client.MixinAllowedAddressResolver" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc120/src/main/resources/mixins.viafabric120.debug.json b/viafabric-mc120/src/main/resources/mixins.viafabric120.debug.json new file mode 100644 index 0000000..10e31a7 --- /dev/null +++ b/viafabric-mc120/src/main/resources/mixins.viafabric120.debug.json @@ -0,0 +1,14 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_16", + "package": "com.viaversion.fabric.mc120.mixin.debug", + "mixins": [ + ], + "client": [ + "client.MixinClientConnectionAccessor", + "client.MixinDebugHud" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc120/src/main/resources/mixins.viafabric120.gui.json b/viafabric-mc120/src/main/resources/mixins.viafabric120.gui.json new file mode 100644 index 0000000..41e90ee --- /dev/null +++ b/viafabric-mc120/src/main/resources/mixins.viafabric120.gui.json @@ -0,0 +1,15 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_16", + "package": "com.viaversion.fabric.mc120.mixin.gui", + "mixins": [ + ], + "client": [ + "client.MixinMultiplayerServerListPingerListener", + "client.MixinServerEntry", + "client.MixinServerInfo" + ], + "injectors": { + "defaultRequire": 0 + } +} diff --git a/viafabric-mc120/src/main/resources/mixins.viafabric120.pipeline.json b/viafabric-mc120/src/main/resources/mixins.viafabric120.pipeline.json new file mode 100644 index 0000000..df9aaa0 --- /dev/null +++ b/viafabric-mc120/src/main/resources/mixins.viafabric120.pipeline.json @@ -0,0 +1,16 @@ +{ + "required": true, + "compatibilityLevel": "JAVA_16", + "package": "com.viaversion.fabric.mc120.mixin.pipeline", + "mixins": [ + "MixinClientConnection", + "MixinServerNetworkIoChInit" + ], + "client": [ + "client.MixinClientConnection", + "client.MixinClientConnectionChInit" + ], + "injectors": { + "defaultRequire": 1 + } +}