Add 1.21 support (#342)

This commit is contained in:
EnZaXD 2024-06-16 18:21:04 +02:00 committed by GitHub
parent 113a6e354a
commit a29320acf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 1871 additions and 5 deletions

View File

@ -10,7 +10,7 @@
Allows the connection to/from different Minecraft versions on your Minecraft client/server (LAN worlds too)
This mod can be installed on 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.4, 1.20.6 with Fabric Loader.
This mod can be installed on 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.4, 1.20.6, 1.21 with Fabric Loader.
## Dependencies

View File

@ -2,10 +2,10 @@
org.gradle.jvmargs=-Xms32M -Xmx4G -XX:+UseG1GC -XX:+UseStringDeduplication
loader_version=0.15.11
viaver_version=5.0.0-SNAPSHOT
viaver_version=5.0.0
yaml_version=2.2
publish_mc_versions=1.20.6, 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
publish_mc_versions=1.21, 1.20.6, 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

View File

@ -19,6 +19,7 @@ include("viafabric-mc1194")
include("viafabric-mc1201")
include("viafabric-mc1204")
include("viafabric-mc1206")
include("viafabric-mc121")
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"

View File

@ -13,8 +13,8 @@
},
"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.1", "1.20.3", "1.20.4", "1.20.5", "1.20.6"],
"viaversion": ">=4.10.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.1", "1.20.3", "1.20.4", "1.20.5", "1.20.6", "1.21"],
"viaversion": ">=5.0.0"
},
"breaks": {
"viafabricplus": "*"

View File

@ -0,0 +1,11 @@
dependencies {
minecraft("com.mojang:minecraft:1.21")
mappings("net.fabricmc:yarn:1.21+build.1:v2")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.100.1+1.21")
modImplementation("com.terraformersmc:modmenu:11.0.0-beta.1")
}
tasks.compileJava {
options.release.set(21)
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121;
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.mc121.commands.VFCommandHandler;
import com.viaversion.fabric.mc121.platform.FabricPlatform;
import com.viaversion.fabric.mc121.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<Void> 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 <S extends CommandSource> LiteralArgumentBuilder<S> command(String commandName) {
return LiteralArgumentBuilder.<S>literal(commandName)
.then(
RequiredArgumentBuilder
.<S, String>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(), JLOGGER);
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");
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121;
import com.viaversion.fabric.mc121.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.ButtonTextures;
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
new ButtonTextures(Identifier.of("viafabric", "widget_unfocused"), Identifier.of("viafabric", "widget_focused")),
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");
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.commands;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
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.Serialization.fromJson(legacy, DynamicRegistryManager.EMPTY);
}
@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().getName().getString();
}
return "?";
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.commands;
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 com.viaversion.fabric.common.commands.subs.LeakDetectSubCommand;
import com.viaversion.viaversion.commands.ViaCommandHandler;
import net.minecraft.command.CommandSource;
import java.util.concurrent.CompletableFuture;
public class VFCommandHandler extends ViaCommandHandler {
{
try {
registerSubCommand(new LeakDetectSubCommand());
} catch (Exception e) {
e.printStackTrace();
}
}
public int execute(CommandContext<? extends CommandSource> 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<Suggestions> suggestion(CommandContext<? extends CommandSource> 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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
return ImmutableMap.of("viafabric", getModConfigScreenFactory());
}
}

View File

@ -0,0 +1,172 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.gui;
import com.viaversion.fabric.common.config.AbstractViaConfigScreen;
import com.viaversion.fabric.common.util.ProtocolUtils;
import com.viaversion.fabric.mc121.ViaFabric;
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.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<Void> 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) {
super.render(drawContext, mouseX, mouseY, delta);
drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 20, 16777215);
}
@Override
public void tick() {
super.tick();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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<Address> resolve(ServerAddress address);
@Inject(method = "resolve", at = @At(value = "HEAD"), cancellable = true)
private void resolveVF(ServerAddress address, CallbackInfoReturnable<Optional<Address>> 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);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.debug;
import com.viaversion.fabric.mc121.ViaFabric;
import net.fabricmc.fabric.impl.registry.sync.RegistrySyncManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
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;
@Mixin(value = RegistrySyncManager.class, remap = false)
public class MixinRegistrySyncManager {
@Inject(method = "configureClient", at = @At("HEAD"), cancellable = true)
private static void ignoreMissingRegistries(ServerConfigurationNetworkHandler handler, MinecraftServer server, CallbackInfo ci) {
if (ViaFabric.config.isIgnoreRegistrySyncErrors()) {
ci.cancel();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.debug.client;
import com.viaversion.fabric.common.handler.CommonTransformer;
import com.viaversion.fabric.common.handler.FabricDecodeHandler;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
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 java.util.List;
@Mixin(DebugHud.class)
public class MixinDebugHud {
@Inject(at = @At("RETURN"), method = "getLeftText")
protected void getLeftText(CallbackInfoReturnable<List<String>> 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) {
UserConnection connection = ((FabricDecodeHandler) viaDecoder).getInfo();
ProtocolInfo protocol = connection.getProtocolInfo();
if (protocol != null) {
ProtocolVersion serverVer = protocol.serverProtocolVersion();
ProtocolVersion clientVer = protocol.protocolVersion();
line += " / C: " + clientVer + " S: " + serverVer + " A: " + connection.isActive();
}
}
info.getReturnValue().add(line);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.debug.client;
import com.viaversion.fabric.mc121.ViaFabric;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.fabricmc.fabric.impl.registry.sync.RegistrySyncManager;
import net.minecraft.util.Identifier;
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;
import java.util.Map;
@Mixin(value = RegistrySyncManager.class, remap = false)
public class MixinRegistrySyncManager {
@Shadow
@Final
private static Logger LOGGER;
@Inject(method = "checkRemoteRemap", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;)V", ordinal = 0), cancellable = true)
private static void ignoreMissingRegistries(Map<Identifier, Object2IntMap<Identifier>> map, CallbackInfo ci) {
if (ViaFabric.config.isIgnoreRegistrySyncErrors()) {
LOGGER.warn("Ignoring missing registries");
ci.cancel();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.gui.client;
import com.viaversion.fabric.common.gui.ViaServerInfo;
import com.viaversion.fabric.common.handler.FabricDecodeHandler;
import com.viaversion.fabric.mc121.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());
}
}
}

View File

@ -0,0 +1,61 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.gui.client;
import com.google.common.collect.Lists;
import com.viaversion.fabric.common.gui.ViaServerInfo;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
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.ModifyArg;
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;
@ModifyArg(method = "render", at = @At(value = "INVOKE", ordinal = 0,
target = "Lnet/minecraft/client/gui/DrawContext;drawGuiTexture(Lnet/minecraft/util/Identifier;IIII)V"))
private Identifier redirectPingIcon(Identifier texture) {
if (((ViaServerInfo) this.server).viaFabric$translating() && texture.getPath().startsWith("server_list/ping")) {
return Identifier.of("viafabric", texture.getPath());
}
return texture;
}
@Redirect(method = "render", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/client/gui/screen/multiplayer/MultiplayerScreen;setTooltip(Lnet/minecraft/text/Text;)V"))
private void addServerVer(MultiplayerScreen instance, Text text) {
ProtocolVersion proto = ProtocolVersion.getProtocol(((ViaServerInfo) this.server).viaFabric$getServerVer());
List<Text> lines = new ArrayList<>();
lines.add(text);
lines.add(Text.translatable("gui.ping_version.translated", proto.getName(), proto.getVersion()));
instance.setTooltip(Lists.transform(lines, Text::asOrderedText));
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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;
}
}

View File

@ -0,0 +1,68 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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.fabric.common.handler.PipelineReorderEvent;
import com.viaversion.fabric.common.protocol.HostnameParserProtocol;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.NetworkSide;
import net.minecraft.network.handler.HandlerNames;
import net.minecraft.network.handler.PacketSizeLogger;
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());
}
@Inject(method = "addHandlers", at = @At("RETURN"))
private static void onAddHandlers(ChannelPipeline pipeline, NetworkSide side, boolean local, PacketSizeLogger packetSizeLogger, CallbackInfo ci) {
final Channel channel = pipeline.channel();
if (channel instanceof SocketChannel) {
final UserConnection user = new UserConnectionImpl(channel, side == NetworkSide.CLIENTBOUND);
final ProtocolPipeline protocolPipeline = new ProtocolPipelineImpl(user);
final boolean clientSide = user.isClientSide();
if (clientSide) {
protocolPipeline.add(HostnameParserProtocol.INSTANCE);
}
pipeline.addBefore(clientSide ? HandlerNames.ENCODER : HandlerNames.OUTBOUND_CONFIG, CommonTransformer.HANDLER_ENCODER_NAME, new FabricEncodeHandler(user));
pipeline.addBefore(clientSide ? HandlerNames.INBOUND_CONFIG : HandlerNames.DECODER, CommonTransformer.HANDLER_DECODER_NAME, new FabricDecodeHandler(user));
}
}
}

View File

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.pipeline.client;
import com.viaversion.fabric.mc121.ViaFabric;
import com.viaversion.fabric.mc121.service.ProtocolAutoDetector;
import io.netty.channel.ChannelFuture;
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(Ljava/net/InetSocketAddress;ZLnet/minecraft/network/ClientConnection;)Lio/netty/channel/ChannelFuture;", at = @At("HEAD"))
private static void onConnect(InetSocketAddress address, boolean useEpoll, ClientConnection connection, CallbackInfoReturnable<ChannelFuture> 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);
}
}
}

View File

@ -0,0 +1,34 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.mixin.shutdown.server;
import com.viaversion.viaversion.ViaManagerImpl;
import com.viaversion.viaversion.api.Via;
import net.minecraft.server.MinecraftServer;
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;
@Mixin(MinecraftServer.class)
public class MixinMinecraftServer {
@Inject(method = "stop", at = @At("TAIL"))
private void onStop(CallbackInfo ci) {
((ViaManagerImpl) Via.getManager()).destroy();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.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.mc121.ViaFabric;
import com.viaversion.fabric.mc121.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<Boolean> 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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.platform;
import com.viaversion.fabric.mc121.providers.FabricVersionProvider;
import com.viaversion.fabric.mc121.providers.VFHandItemProvider;
import com.viaversion.fabric.mc121.providers.VFPlayerAbilitiesProvider;
import com.viaversion.fabric.mc121.providers.VFPlayerLookTargetProvider;
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.v1_12_2to1_13.provider.PlayerLookTargetProvider;
import com.viaversion.viaversion.protocols.v1_15_2to1_16.provider.PlayerAbilitiesProvider;
import com.viaversion.viaversion.protocols.v1_8to1_9.provider.HandItemProvider;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.providers;
import com.viaversion.fabric.common.config.VFConfig;
import com.viaversion.fabric.common.provider.AbstractFabricVersionProvider;
import com.viaversion.fabric.mc121.ViaFabric;
import com.viaversion.fabric.mc121.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<ProtocolVersion> 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");
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.providers;
import com.viaversion.fabric.common.util.RemappingUtil;
import com.viaversion.fabric.mc121.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.v1_8to1_9.provider.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, null);
}
return clientItem.copy();
}
@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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.providers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_15_2to1_16.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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.providers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.provider.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 BlockPosition getPlayerLookTarget(UserConnection info) {
if (!info.isClientSide()) return null;
if (MinecraftClient.getInstance().crosshairTarget instanceof BlockHitResult blockHitResult) {
final BlockPos pos = blockHitResult.getBlockPos();
return new BlockPosition(pos.getX(), pos.getY(), pos.getZ());
}
return null;
}
}

View File

@ -0,0 +1,160 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.fabric.mc121.service;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.viaversion.fabric.common.AddressParser;
import com.viaversion.fabric.mc121.ViaFabric;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
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.SharedConstants;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.DisconnectionInfo;
import net.minecraft.network.NetworkSide;
import net.minecraft.network.handler.EncoderHandler;
import net.minecraft.network.handler.NetworkStateTransitions;
import net.minecraft.network.handler.SizePrepender;
import net.minecraft.network.handler.SplitterHandler;
import net.minecraft.network.listener.ClientQueryPacketListener;
import net.minecraft.network.packet.c2s.handshake.ConnectionIntent;
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
import net.minecraft.network.packet.c2s.query.QueryRequestC2SPacket;
import net.minecraft.network.packet.s2c.query.PingResultS2CPacket;
import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket;
import net.minecraft.network.state.HandshakeStates;
import net.minecraft.network.state.QueryStates;
import net.minecraft.server.ServerMetadata;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
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<InetSocketAddress, CompletableFuture<ProtocolVersion>> SERVER_VER = CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.build(CacheLoader.from((address) -> {
CompletableFuture<ProtocolVersion> 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<>() {
@Override
protected void initChannel(@NotNull 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(null))
.addLast("inbound_config", new NetworkStateTransitions.InboundConfigurer())
.addLast("prepender", new SizePrepender())
.addLast("encoder", new EncoderHandler<>(HandshakeStates.C2S))
.addLast("packet_handler", clientConnection);
}
})
.connect(address);
ch.addListener(future1 -> {
if (!future1.isSuccess()) {
future.completeExceptionally(future1.cause());
} else {
ch.channel().eventLoop().submit(() -> { // needs to execute after channel init
clientConnection.transitionInbound(QueryStates.S2C, 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 onPingResult(PingResultS2CPacket packet) {
clientConnection.disconnect(Text.literal("Pong not requested!"));
}
@Override
public void onDisconnected(DisconnectionInfo info) {
future.completeExceptionally(new IllegalStateException(info.reason().getString()));
}
@Override
public boolean isConnectionOpen() {
return ch.channel().isOpen();
}
});
//noinspection deprecation
clientConnection.send(new HandshakeC2SPacket(
SharedConstants.getGameVersion().getProtocolVersion(),
address.getHostString(),
address.getPort(),
ConnectionIntent.STATUS
));
clientConnection.transitionOutbound(QueryStates.C2S);
clientConnection.send(QueryRequestC2SPacket.INSTANCE);
});
}
});
} catch (Throwable throwable) {
future.completeExceptionally(throwable);
}
return future;
}));
public static CompletableFuture<ProtocolVersion> 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);
}
}
}

View File

@ -0,0 +1,79 @@
{
"schemaVersion": 1,
"id": "viafabric-mc121",
"name": "ViaFabric for 1.21",
"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.mc121.ViaFabric"
],
"client": [
"com.viaversion.fabric.mc121.ViaFabricClient"
],
"modmenu": [
"com.viaversion.fabric.mc121.gui.ModMenuConfig"
]
},
"depends": {
"fabric-resource-loader-v0": "*",
"minecraft": ["1.21"],
"viafabric": "*"
},
"recommends": {
"fabric-command-api-v2": "*"
},
"mixins": [
"mixins.viafabric121.address.json",
"mixins.viafabric121.gui.json",
"mixins.viafabric121.debug.json",
"mixins.viafabric121.pipeline.json",
"mixins.viafabric121.shutdown.json"
],
"custom": {
"modmenu": {
"parent": "viafabric"
}
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"compatibilityLevel": "JAVA_21",
"package": "com.viaversion.fabric.mc121.mixin.address",
"mixins": [
],
"client": [
"client.MixinAllowedAddressResolver"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,17 @@
{
"required": true,
"compatibilityLevel": "JAVA_21",
"package": "com.viaversion.fabric.mc121.mixin.debug",
"mixins": [
"MixinRegistrySyncManager"
],
"client": [
"client.MixinRegistrySyncManager",
"client.MixinClientConnection",
"client.MixinClientConnectionAccessor",
"client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,15 @@
{
"required": true,
"compatibilityLevel": "JAVA_21",
"package": "com.viaversion.fabric.mc121.mixin.gui",
"mixins": [
],
"client": [
"client.MixinMultiplayerServerListPingerListener",
"client.MixinServerEntry",
"client.MixinServerInfo"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"compatibilityLevel": "JAVA_21",
"package": "com.viaversion.fabric.mc121.mixin.pipeline",
"mixins": [
"MixinClientConnection"
],
"client": [
"client.MixinClientConnection"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"compatibilityLevel": "JAVA_21",
"package": "com.viaversion.fabric.mc121.mixin.shutdown",
"mixins": [
],
"server": [
"server.MixinMinecraftServer"
],
"injectors": {
"defaultRequire": 0
}
}