subprojects part 2

This commit is contained in:
creeper123123321 2021-05-02 12:01:24 -03:00
parent e49f1539ed
commit c09d6f0a74
101 changed files with 4716 additions and 66 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
custom: ['https://viaversion.com/donate', 'https://viaversion.com/backwards', 'https://viaversion.com/rewind', 'https://creeper123123321.keybase.pub/#donate']
custom: ['https://viaversion.com/donate', 'https://viaversion.com/backwards', 'https://viaversion.com/rewind', 'https://creeper123123321.github.io/#donate']

View File

@ -18,7 +18,7 @@ jobs:
java-version: 11
- name: build
env:
curse_api_key: ${{ secrets.CREEPER_CF }}
CURSEFORGE_API_KEY: ${{ secrets.CREEPER_CF }}
run: ./gradlew
- name: capture build artifacts
uses: actions/upload-artifact@v2

View File

@ -16,7 +16,6 @@ class Globals {
version = Globals.baseVersion + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()
logger.lifecycle("Building ViaFabric: " + version)
import org.apache.commons.codec.digest.DigestUtils
import net.fabricmc.loom.task.RunClientTask
import net.fabricmc.loom.task.RunServerTask
@ -118,13 +117,13 @@ allprojects {
}
jar {
classifier = "dev"
archiveClassifier = "dev"
}
afterEvaluate {
remapJar {
input = file("${project.buildDir}/libs/$archivesBaseName-${version}-dev.jar")
archiveName = "${archivesBaseName}-${version}.jar"
archiveFileName = "${archivesBaseName}-${version}.jar"
}
artifacts {
@ -220,7 +219,7 @@ subprojects {
task remapMavenJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: jar) {
afterEvaluate {
input = file("${project.buildDir}/libs/${archivesBaseName}-${version}-dev.jar")
archiveName = "${archivesBaseName}-${version}-maven.jar"
archiveFileName = "${archivesBaseName}-${version}-maven.jar"
addNestedDependencies = false
}
}
@ -279,6 +278,10 @@ dependencies {
mappings("net.fabricmc:yarn:1.8.9+build.202103291533:v2")
modCompile("net.fabricmc:fabric-loader:0.10.5+build.213")
include("com.viaversion:viaversion:4.0.0-21w17a")
include("org.yaml:snakeyaml:1.28")
include("io.github.cottonmc:cotton-client-commands:1.0.0+1.15.2")
afterEvaluate {
subprojects.each {
compile project(path: ":${it.name}", configuration: "dev")
@ -298,11 +301,16 @@ curseforge {
id = "391298"
changelog = "A changelog can be found at https://github.com/ViaVersion/ViaFabric/commits"
releaseType = "alpha"
addGameVersion "1.17"
addGameVersion "Fabric"
Arrays.<String> asList("1.17", "1.16.5", "1.15.2", "1.14.4", "1.8.9", "Java 8", "Java 9", "Java 10", "Fabric")
.forEach { ver -> addGameVersion(ver) }
mainArtifact(file("${project.buildDir}/libs/${archivesBaseName}-${version}.jar")) {
displayName = "[${getBranch()}] ViaFabric $Globals.baseVersion"
relations {
requiredDependency("fabric-api")
requiredDependency("legacy-fabric-api")
embeddedLibrary("cotton-client-commands")
}
}
afterEvaluate {
@ -314,3 +322,9 @@ curseforge {
forgeGradleIntegration = false
}
}
if (getBranch() == "master" && ENV.CURSEFORGE_API_KEY && !ENV.CURSEFORGE_API_KEY.isEmpty()) {
defaultTasks("clean", "build", "curseforge")
} else {
defaultTasks("clean", "build")
}

View File

@ -12,4 +12,6 @@ rootProject.name = "viafabric"
include "viafabric-mc18"
include "viafabric-mc114"
include "viafabric-mc115"
include "viafabric-mc116"
include "viafabric-mc117"

View File

@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"id": "viafabric-impl",
"id": "viafabric-mc114",
"name": "ViaFabric",
"version": "@version@",
"description": "@description@",

View File

@ -1,13 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc114.mixin.address",
"mixins": [
],
"client": [
"client.MixinConnectScreenThread",
"client.MixinServerAddress",
"client.MixinServerPinger"
"com.viaversion.fabric.mc114.mixin.address.client.MixinConnectScreenThread",
"com.viaversion.fabric.mc114.mixin.address.client.MixinServerAddress",
"com.viaversion.fabric.mc114.mixin.address.client.MixinServerPinger"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,12 +1,11 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc114.mixin.debug",
"mixins": [
],
"client": [
"client.MixinClientConnectionAccessor",
"client.MixinDebugHud"
"com.viaversion.fabric.mc114.mixin.debug.client.MixinClientConnectionAccessor",
"com.viaversion.fabric.mc114.mixin.debug.client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0

View File

@ -1,11 +1,10 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc114.mixin.gui",
"mixins": [
],
"client": [
"client.MixinMultiplayerScreen"
"com.viaversion.fabric.mc114.mixin.gui.client.MixinMultiplayerScreen"
],
"injectors": {
"defaultRequire": 0

View File

@ -1,13 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc114.mixin.pipeline",
"mixins": [
"MixinClientConnection",
"MixinServerNetworkIoChInit"
"com.viaversion.fabric.mc114.mixin.pipeline.MixinClientConnection",
"com.viaversion.fabric.mc114.mixin.pipeline.MixinServerNetworkIoChInit"
],
"client": [
"client.MixinClientConnectionChInit"
"com.viaversion.fabric.mc114.mixin.pipeline.client.MixinClientConnectionChInit"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,11 +1,10 @@
version = rootProject.version
dependencies {
minecraft("com.mojang:minecraft:1.14.4")
mappings("net.fabricmc:yarn:1.14.4+build.16:v2")
modImplementation("net.fabricmc:fabric-loader:0.8.2+build.194")
minecraft("com.mojang:minecraft:1.15.2")
mappings("net.fabricmc:yarn:1.15.2+build.17:v2")
modImplementation("net.fabricmc:fabric-loader:0.9.3+build.207")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.13.1+build.257-1.14")
modImplementation("io.github.prospector:modmenu:1.7.16.1.14.4+build.128")
modImplementation("io.github.cottonmc:cotton-client-commands:1.0.0+1.15.2")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.19.0+build.325-1.15")
modImplementation("io.github.prospector:modmenu:1.10.2+build.32")
}

View File

@ -0,0 +1,110 @@
package com.viaversion.fabric.mc115;
import com.viaversion.fabric.mc115.commands.VRCommandHandler;
import com.viaversion.fabric.mc115.config.VRConfig;
import com.viaversion.fabric.mc115.platform.VRInjector;
import com.viaversion.fabric.mc115.platform.VRLoader;
import com.viaversion.fabric.mc115.platform.VRPlatform;
import com.viaversion.fabric.mc115.protocol.ViaFabricHostnameProtocol;
import com.viaversion.fabric.mc115.util.JLoggerToLog4j;
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 io.netty.channel.DefaultEventLoop;
import io.netty.channel.EventLoop;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.command.CommandSource;
import org.apache.logging.log4j.LogManager;
import com.viaversion.viaversion.ViaManagerImpl;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
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 CompletableFuture<Void> INIT_FUTURE = new CompletableFuture<>();
public static VRConfig 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 String getVersion() {
return FabricLoader.getInstance().getModContainer("viafabric")
.get().getMetadata().getVersion().getFriendlyString();
}
public static <S extends CommandSource> LiteralArgumentBuilder<S> command(String commandName) {
return LiteralArgumentBuilder.<S>literal(commandName)
.then(
RequiredArgumentBuilder
.<S, String>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() {
Via.init(ViaManagerImpl.builder()
.injector(new VRInjector())
.loader(new VRLoader())
.commandHandler(new VRCommandHandler())
.platform(new VRPlatform()).build());
FabricLoader.getInstance().getModContainer("viabackwards").ifPresent(mod -> MappingDataLoader.enableMappingsCache());
((ViaManagerImpl) Via.getManager()).init();
Via.getManager().getProtocolManager().registerBaseProtocol(ViaFabricHostnameProtocol.INSTANCE, Range.lessThan(Integer.MIN_VALUE));
ProtocolVersion.register(-2, "AUTO");
FabricLoader.getInstance().getEntrypoints("viafabric:via_api_initialized", Runnable.class).forEach(Runnable::run);
try {
registerCommandsV1();
} catch (NoClassDefFoundError ignored) {
try {
registerCommandsV0();
JLOGGER.info("Using Fabric Commands V0");
} catch (NoClassDefFoundError ignored2) {
JLOGGER.info("Couldn't register command as Fabric Commands isn't installed");
}
}
config = new VRConfig(FabricLoader.getInstance().getConfigDir().resolve("ViaFabric")
.resolve("viafabric.yml").toFile());
INIT_FUTURE.complete(null);
}
private void registerCommandsV1() {
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("viaversion")));
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("viaver")));
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("vvfabric")));
}
@SuppressWarnings("deprecation")
private void registerCommandsV0() {
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("viaversion")));
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("viaver")));
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("vvfabric")));
}
}

View File

@ -0,0 +1,77 @@
package com.viaversion.fabric.mc115;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.Locale;
public class ViaFabricAddress {
public int protocol = 0;
public String viaSuffix = null;
public String realAddress = null;
public ViaFabricAddress parse(String address) {
if (address == null) return null;
String[] parts = address.split("\\.");
boolean foundDomain = false;
boolean foundOptions = false;
StringBuilder ourParts = new StringBuilder();
StringBuilder realAddrBuilder = new StringBuilder();
for (int i = parts.length - 1; i >= 0; i--) {
String part = parts[i];
boolean realAddrPart = false;
if (foundDomain) {
if (!foundOptions) {
if (part.startsWith("_")) {
String arg = part.substring(2);
if (part.toLowerCase(Locale.ROOT).startsWith("_v")) {
try {
protocol = Integer.parseInt(arg);
} catch (NumberFormatException e) {
ProtocolVersion closest = ProtocolVersion.getClosest(arg.replace("_", "."));
if (closest != null) {
protocol = closest.getVersion();
}
}
}
} else {
foundOptions = true;
}
}
if (foundOptions) {
realAddrPart = true;
}
} else if (part.equalsIgnoreCase("viafabric")) {
foundDomain = true;
}
if (realAddrPart) {
realAddrBuilder.insert(0, part + ".");
} else {
ourParts.insert(0, part + ".");
}
}
String realAddr = realAddrBuilder.toString().replaceAll("\\.$", "");
String suffix = ourParts.toString().replaceAll("\\.$", "");
if (realAddr.isEmpty()) {
this.realAddress = address;
} else {
this.realAddress = realAddr;
this.viaSuffix = suffix;
}
return this;
}
@Override
public String toString() {
return "ViaFabricAddress{" +
"protocol=" + protocol +
", viaSuffix='" + viaSuffix + '\'' +
", realAddress='" + realAddress + '\'' +
'}';
}
}

View File

@ -0,0 +1,61 @@
package com.viaversion.fabric.mc115.commands;
import io.github.cottonmc.clientcommands.CottonClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.server.command.CommandSource;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
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);
}
@Override
public void sendMessage(String s) {
if (source instanceof ServerCommandSource) {
((ServerCommandSource) source).sendFeedback(Text.Serializer.fromJson(legacyToJson(s)), false);
} else if (source instanceof CottonClientCommandSource) {
((CottonClientCommandSource) source).sendFeedback(Text.Serializer.fromJson(legacyToJson(s)), false);
}
}
private String legacyToJson(String legacy) {
return GsonComponentSerializer.gson().serialize(LegacyComponentSerializer.legacySection().deserialize(legacy));
}
@Override
public UUID getUUID() {
if (source instanceof ServerCommandSource) {
Entity entity = ((ServerCommandSource) source).getEntity();
if (entity != null) return entity.getUuid();
} else if (source instanceof CottonClientCommandSource) {
return MinecraftClient.getInstance().player.getUuid();
}
return UUID.fromString(getName());
}
@Override
public String getName() {
if (source instanceof ServerCommandSource) {
return ((ServerCommandSource) source).getName();
} else if (source instanceof CottonClientCommandSource) {
return MinecraftClient.getInstance().player.getEntityName();
}
return "?";
}
}

View File

@ -0,0 +1,35 @@
package com.viaversion.fabric.mc115.commands;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.connection.UserConnection;
import java.util.UUID;
public class UserCommandSender implements ViaCommandSender {
private UserConnection con;
public UserCommandSender(UserConnection con) {
this.con = con;
}
@Override
public boolean hasPermission(String s) {
return false;
}
@Override
public void sendMessage(String s) {
Via.getPlatform().sendMessage(getUUID(), s);
}
@Override
public UUID getUUID() {
return con.getProtocolInfo().getUuid();
}
@Override
public String getName() {
return con.getProtocolInfo().getUsername();
}
}

View File

@ -0,0 +1,13 @@
package com.viaversion.fabric.mc115.commands;
import com.viaversion.fabric.mc115.ViaFabric;
import com.mojang.brigadier.CommandDispatcher;
import io.github.cottonmc.clientcommands.ClientCommandPlugin;
import io.github.cottonmc.clientcommands.CottonClientCommandSource;
public class VRClientCommands implements ClientCommandPlugin {
@Override
public void registerCommands(CommandDispatcher<CottonClientCommandSource> commandDispatcher) {
commandDispatcher.register(ViaFabric.command("viafabricclient"));
}
}

View File

@ -0,0 +1,55 @@
package com.viaversion.fabric.mc115.commands;
import com.viaversion.fabric.mc115.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.server.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<? 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,48 @@
package com.viaversion.fabric.mc115.commands.subs;
import io.netty.util.ResourceLeakDetector;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LeakDetectSubCommand extends ViaSubCommand {
@Override
public String name() {
return "leakdetect";
}
@Override
public String description() {
return "Sets ResourceLeakDetector level";
}
@Override
public boolean execute(ViaCommandSender viaCommandSender, String[] strings) {
if (strings.length == 1) {
try {
ResourceLeakDetector.Level level = ResourceLeakDetector.Level.valueOf(strings[0]);
ResourceLeakDetector.setLevel(level);
viaCommandSender.sendMessage("Set leak detector level to " + level);
} catch (IllegalArgumentException e) {
viaCommandSender.sendMessage("Invalid level (" + Arrays.toString(ResourceLeakDetector.Level.values()) + ")");
}
} else {
viaCommandSender.sendMessage("Current leak detection level is " + ResourceLeakDetector.getLevel());
}
return true;
}
@Override
public List<String> onTabComplete(ViaCommandSender sender, String[] args) {
if (args.length == 1) {
return Arrays.stream(ResourceLeakDetector.Level.values())
.map(Enum::name)
.filter(it -> it.startsWith(args[0]))
.collect(Collectors.toList());
}
return super.onTabComplete(sender, args);
}
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc115.config;
import com.viaversion.viaversion.util.Config;
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class VRConfig extends Config {
public static final String ENABLE_CLIENT_SIDE = "enable-client-side";
public static final String CLIENT_SIDE_VERSION = "client-side-version";
public static final String CLIENT_SIDE_FORCE_DISABLE = "client-side-force-disable";
public static final String HIDE_BUTTON = "hide-button";
public VRConfig(File configFile) {
super(configFile);
reloadConfig();
}
@Override
public URL getDefaultConfigURL() {
return getClass().getClassLoader().getResource("assets/viafabric/config.yml");
}
@Override
protected void handleConfig(Map<String, Object> map) {
}
@Override
public List<String> getUnsupportedOptions() {
return Collections.emptyList();
}
public boolean isClientSideEnabled() {
return getBoolean(ENABLE_CLIENT_SIDE, false);
}
public void setClientSideEnabled(boolean val) {
set(ENABLE_CLIENT_SIDE, val);
}
public int getClientSideVersion() {
return getInt(CLIENT_SIDE_VERSION, -1);
}
public void setClientSideVersion(int val) {
set(CLIENT_SIDE_VERSION, val);
}
public Collection<?> getClientSideForceDisable() {
return (List<?>) get(CLIENT_SIDE_FORCE_DISABLE, List.class, Collections.emptyList());
}
public void setHideButton(boolean val) {
set(HIDE_BUTTON, val);
}
public boolean isHideButton() {
return getBoolean(HIDE_BUTTON, false);
}
public boolean isForcedDisable(String line) {
return getClientSideForceDisable().contains(line);
}
}

View File

@ -0,0 +1,16 @@
package com.viaversion.fabric.mc115.gui;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
public class ModMenuConfig implements ModMenuApi {
@Override
public String getModId() {
return "viafabric";
}
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return ViaConfigScreen::new;
}
}

View File

@ -0,0 +1,169 @@
package com.viaversion.fabric.mc115.gui;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.util.ProtocolUtils;
import com.viaversion.viaversion.api.Via;
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.text.TranslatableText;
import java.util.concurrent.CompletableFuture;
@Environment(EnvType.CLIENT)
public class ViaConfigScreen extends Screen {
private static CompletableFuture<Void> latestProtocolSave;
private final Screen parent;
private TextFieldWidget protocolVersion;
public ViaConfigScreen(Screen parent) {
super(new TranslatableText("gui.viafabric_config.title"));
this.parent = parent;
}
private static int getProtocolTextColor(boolean valid, boolean supported) {
if (!valid) {
return 0xff0000; // Red
} else if (!supported) {
return 0xFFA500; // Orange
}
return 0xE0E0E0; // Default
}
@Override
protected void init() {
int entries = 0;
this.addButton(new ButtonWidget(this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, getClientSideText().asString(), this::onClickClientSide));
entries++;
this.addButton(new ButtonWidget(this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, getHideViaButtonText().asString(), this::onHideViaButton));
entries++;
protocolVersion = new TextFieldWidget(this.font,
this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, new TranslatableText("gui.protocol_version_field.name").asString());
entries++;
protocolVersion.setTextPredicate(ProtocolUtils::isStartOfProtocolText);
protocolVersion.setChangedListener(this::onChangeVersionField);
int clientSideVersion = ViaFabric.config.getClientSideVersion();
protocolVersion.setText(ProtocolUtils.getProtocolName(clientSideVersion));
this.children.add(protocolVersion);
//noinspection ConstantConditions
if (entries % 2 == 1) {
entries++;
}
this.addButton(new ButtonWidget(this.width / 2 - 100, this.height / 6 + 24 * (entries >> 1), 200, 20, new TranslatableText("gui.done").asString(), (buttonWidget) -> MinecraftClient.getInstance().openScreen(this.parent)));
}
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(ProtocolUtils.isSupported(newVersion, Via.getAPI().getServerVersion().lowestSupportedVersion()),
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().openScreen(new ConfirmScreen(
answer -> {
if (answer) {
ViaFabric.config.setClientSideEnabled(true);
ViaFabric.config.setClientSideVersion(-2); // AUTO
ViaFabric.config.saveConfig();
widget.setMessage(getClientSideText().asString());
}
MinecraftClient.getInstance().openScreen(this);
},
new TranslatableText("gui.enable_client_side.question"),
new TranslatableText("gui.enable_client_side.warning"),
new TranslatableText("gui.enable_client_side.enable").asString(),
new TranslatableText("gui.cancel").asString()
));
} else {
ViaFabric.config.setClientSideEnabled(false);
ViaFabric.config.saveConfig();
}
widget.setMessage(getClientSideText().asString());
}
@Override
public void removed() {
ViaFabric.config.saveConfig();
}
@Override
public void onClose() {
MinecraftClient.getInstance().openScreen(this.parent);
}
private TranslatableText getClientSideText() {
return ViaFabric.config.isClientSideEnabled() ?
new TranslatableText("gui.client_side.disable")
: new TranslatableText("gui.client_side.enable");
}
private TranslatableText getHideViaButtonText() {
return ViaFabric.config.isHideButton() ?
new TranslatableText("gui.hide_via_button.disable") : new TranslatableText("gui.hide_via_button.enable");
}
private void onHideViaButton(ButtonWidget widget) {
ViaFabric.config.setHideButton(!ViaFabric.config.isHideButton());
ViaFabric.config.saveConfig();
widget.setMessage(getHideViaButtonText().asString());
}
@Override
public void render(int mouseX, int mouseY, float delta) {
this.renderBackground();
drawCenteredString(this.font, this.title.asString(), this.width / 2, 20, 16777215);
super.render(mouseX, mouseY, delta);
protocolVersion.render(mouseX, mouseY, delta);
}
@Override
public void tick() {
super.tick();
protocolVersion.tick();
}
}

View File

@ -0,0 +1,38 @@
package com.viaversion.fabric.mc115.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
public class CommonTransformer {
public static final String HANDLER_DECODER_NAME = "via-decoder";
public static final String HANDLER_ENCODER_NAME = "via-encoder";
public static void decompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
ChannelHandler handler = ctx.pipeline().get("decompress");
ByteBuf decompressed = handler instanceof MessageToMessageDecoder
? (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) handler, ctx, buf).get(0)
: (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) handler, ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
}
public static void compress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compress"), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
}

View File

@ -0,0 +1,85 @@
package com.viaversion.fabric.mc115.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelDecoderException;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@ChannelHandler.Sharable
public class FabricDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection info;
private boolean handledCompression;
private boolean skipDoubleTransform;
public FabricDecodeHandler(UserConnection info) {
this.info = info;
}
public UserConnection getInfo() {
return info;
}
// https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (skipDoubleTransform) {
skipDoubleTransform = false;
out.add(bytebuf.retain());
return;
}
if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformIncoming(transformedBuf, CancelDecoderException::generate);
if (needsCompress) {
CommonTransformer.compress(ctx, transformedBuf);
skipDoubleTransform = true;
}
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int decoderIndex = ctx.pipeline().names().indexOf("decompress");
if (decoderIndex == -1) return false;
handledCompression = true;
if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) {
// Need to decompress this packet due to bad order
CommonTransformer.decompress(ctx, buf);
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compress", "via-encoder", encoder);
ctx.pipeline().addAfter("decompress", "via-decoder", decoder);
return true;
}
return false;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return;
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,72 @@
package com.viaversion.fabric.mc115.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelEncoderException;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@ChannelHandler.Sharable
public class FabricEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
private final UserConnection info;
private boolean handledCompression;
public FabricEncodeHandler(UserConnection info) {
this.info = info;
}
@Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformOutgoing(transformedBuf, CancelEncoderException::generate);
if (needsCompress) {
CommonTransformer.compress(ctx, transformedBuf);
}
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int encoderIndex = ctx.pipeline().names().indexOf("compress");
if (encoderIndex == -1) return false;
handledCompression = true;
if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order
CommonTransformer.decompress(ctx, buf);
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compress", "via-encoder", encoder);
ctx.pipeline().addAfter("decompress", "via-decoder", decoder);
return true;
}
return false;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return;
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,79 @@
package com.viaversion.fabric.mc115.handler.clientside;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.service.ProtocolAutoDetector;
import com.viaversion.viaversion.util.Pair;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import java.net.InetSocketAddress;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ProtocolDetectionHandler extends ChannelDuplexHandler {
private final Queue<Pair<Object, ChannelPromise>> queuedMessages = new ArrayDeque<>();
private boolean hold = true;
private boolean pendentFlush;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
if (ctx.channel().remoteAddress() instanceof InetSocketAddress) {
ScheduledFuture<?> timeoutRun = ctx.executor().schedule(() -> {
ViaFabric.JLOGGER.warning("Timeout for protocol auto-detection in "
+ ctx.channel().remoteAddress() + " server");
hold = false;
drainQueue(ctx);
ctx.pipeline().remove(this);
}, 10, TimeUnit.SECONDS);
ProtocolAutoDetector.detectVersion(((InetSocketAddress) ctx.channel().remoteAddress()))
.whenComplete((obj, ex) -> {
ctx.pipeline().remove(this);
timeoutRun.cancel(false);
});
// Let's cache it before we need it
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!hold) {
drainQueue(ctx);
super.write(ctx, msg, promise);
} else {
queuedMessages.add(new Pair<>(msg, promise));
}
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
if (!hold) {
drainQueue(ctx);
super.flush(ctx);
} else {
pendentFlush = true;
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
drainQueue(ctx);
super.channelInactive(ctx);
}
private void drainQueue(ChannelHandlerContext ctx) {
queuedMessages.forEach(it -> ctx.write(it.getKey(), it.getValue()));
queuedMessages.clear();
if (pendentFlush) ctx.flush();
pendentFlush = false;
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
drainQueue(ctx);
super.handlerRemoved(ctx);
}
}

View File

@ -0,0 +1,24 @@
package com.viaversion.fabric.mc115.mixin.address.client;
import com.viaversion.fabric.mc115.ViaFabricAddress;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Mixin(targets = "net/minecraft/client/gui/screen/ConnectScreen$1", priority = 2000)
public class MixinConnectScreenThread {
@Redirect(method = "run()V", at = @At(value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"))
private InetAddress resolveViaFabricAddr(String address) throws UnknownHostException {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return InetAddress.getByName(address);
}
InetAddress resolved = InetAddress.getByName(viaAddr.realAddress);
return InetAddress.getByAddress(resolved.getHostName() + "." + viaAddr.viaSuffix, resolved.getAddress());
}
}

View File

@ -0,0 +1,29 @@
package com.viaversion.fabric.mc115.mixin.address.client;
import com.viaversion.fabric.mc115.ViaFabricAddress;
import net.minecraft.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.Redirect;
@Mixin(ServerAddress.class)
public abstract class MixinServerAddress {
@Shadow
private static String[] resolveSrv(String address) {
throw new AssertionError();
}
@Redirect(method = "parse", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ServerAddress;resolveSrv(Ljava/lang/String;)[Ljava/lang/String;"))
private static String[] modifySrvAddr(String address) {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return resolveSrv(address);
}
String[] resolvedSrv = resolveSrv(viaAddr.realAddress);
resolvedSrv[0] = resolvedSrv[0].replaceAll("\\.$", "") + "." + viaAddr.viaSuffix;
return resolvedSrv;
}
}

View File

@ -0,0 +1,25 @@
package com.viaversion.fabric.mc115.mixin.address.client;
import com.viaversion.fabric.mc115.ViaFabricAddress;
import net.minecraft.client.network.MultiplayerServerListPinger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Mixin(MultiplayerServerListPinger.class)
public class MixinServerPinger {
@Redirect(method = "add", at = @At(value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"))
private InetAddress resolveViaFabricAddr(String address) throws UnknownHostException {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return InetAddress.getByName(address);
}
InetAddress resolved = InetAddress.getByName(viaAddr.realAddress);
return InetAddress.getByAddress(resolved.getHostName() + "." + viaAddr.viaSuffix, resolved.getAddress());
}
}

View File

@ -0,0 +1,12 @@
package com.viaversion.fabric.mc115.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,38 @@
package com.viaversion.fabric.mc115.mixin.debug.client;
import com.viaversion.fabric.mc115.handler.CommonTransformer;
import com.viaversion.fabric.mc115.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<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) {
ProtocolInfo protocol = ((FabricDecodeHandler) viaDecoder).getInfo().getProtocolInfo();
if (protocol != null) {
ProtocolVersion serverVer = ProtocolVersion.getProtocol(protocol.getServerProtocolVersion());
ProtocolVersion clientVer = ProtocolVersion.getProtocol(protocol.getProtocolVersion());
line += " / C: " + clientVer.getName() + " (" + clientVer.getVersion() + ") S: "
+ serverVer.getName() + " (" + serverVer.getVersion() + ") A: " + protocol.getUser().isActive();
}
}
info.getReturnValue().add(line);
}
}

View File

@ -0,0 +1,38 @@
package com.viaversion.fabric.mc115.mixin.gui.client;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.gui.ViaConfigScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
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.text.TranslatableText;
import net.minecraft.util.Identifier;
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(MultiplayerScreen.class)
public abstract class MixinMultiplayerScreen extends Screen {
protected MixinMultiplayerScreen(Text title, UnsupportedOperationException e) {
super(title);
throw e;
}
@Inject(method = "init", at = @At("TAIL"), remap = false)
private void onInit(CallbackInfo ci) {
ButtonWidget enableClientSideViaVersion = new TexturedButtonWidget(this.width / 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().openScreen(new ViaConfigScreen(this)),
new TranslatableText("gui.via_button").asString());
if (ViaFabric.config.isHideButton()) enableClientSideViaVersion.visible = false;
addButton(enableClientSideViaVersion);
}
}

View File

@ -0,0 +1,31 @@
package com.viaversion.fabric.mc115.mixin.pipeline;
import io.netty.channel.Channel;
import net.minecraft.network.ClientConnection;
import org.apache.logging.log4j.Logger;
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;
@Mixin(ClientConnection.class)
public class MixinClientConnection {
@Shadow
private Channel channel;
@Redirect(
method = "exceptionCaught",
remap = false,
at = @At(
value = "INVOKE",
target = "Lorg/apache/logging/log4j/Logger;debug(Ljava/lang/String;Ljava/lang/Throwable;)V"
))
private void redirectDebug(Logger logger, String message, Throwable t) {
if ("Failed to sent packet".equals(message)) {
logger.info(message, t);
} else {
logger.debug(message, t);
}
}
}

View File

@ -0,0 +1,28 @@
package com.viaversion.fabric.mc115.mixin.pipeline;
import com.viaversion.fabric.mc115.handler.CommonTransformer;
import com.viaversion.fabric.mc115.handler.FabricDecodeHandler;
import com.viaversion.fabric.mc115.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));
}
}
}

View File

@ -0,0 +1,35 @@
package com.viaversion.fabric.mc115.mixin.pipeline.client;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.handler.CommonTransformer;
import com.viaversion.fabric.mc115.handler.FabricDecodeHandler;
import com.viaversion.fabric.mc115.handler.FabricEncodeHandler;
import com.viaversion.fabric.mc115.handler.clientside.ProtocolDetectionHandler;
import com.viaversion.fabric.mc115.protocol.ViaFabricHostnameProtocol;
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(ViaFabricHostnameProtocol.INSTANCE);
channel.pipeline()
.addBefore("encoder", CommonTransformer.HANDLER_ENCODER_NAME, new FabricEncodeHandler(user))
.addBefore("decoder", CommonTransformer.HANDLER_DECODER_NAME, new FabricDecodeHandler(user));
if (ViaFabric.config.isClientSideEnabled()) {
channel.pipeline().addAfter(CommonTransformer.HANDLER_ENCODER_NAME, "via-autoprotocol", new ProtocolDetectionHandler());
}
}
}
}

View File

@ -0,0 +1,61 @@
package com.viaversion.fabric.mc115.platform;
import com.viaversion.fabric.mc115.handler.CommonTransformer;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.util.GsonUtil;
import com.viaversion.viaversion.libs.gson.JsonObject;
import java.lang.reflect.Method;
import java.util.Arrays;
public class VRInjector implements ViaInjector {
@Override
public void inject() {
// *looks at Mixins*
}
@Override
public void uninject() {
// not possible *plays sad violin*
}
@Override
public int getServerProtocolVersion() {
return SharedConstants.getGameVersion().getProtocolVersion();
}
@Override
public String getEncoderName() {
return CommonTransformer.HANDLER_ENCODER_NAME;
}
@Override
public String getDecoderName() {
return CommonTransformer.HANDLER_DECODER_NAME;
}
@Override
public JsonObject getDump() {
JsonObject obj = new JsonObject();
try {
obj.add("serverNetworkIOChInit", GsonUtil.getGson().toJsonTree(
Arrays.stream(Class.forName("net.minecraft.class_3242$1").getDeclaredMethods())
.map(Method::toString)
.toArray(String[]::new)));
} catch (ClassNotFoundException ignored) {
}
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
try {
obj.add("clientConnectionChInit", GsonUtil.getGson().toJsonTree(
Arrays.stream(Class.forName("net.minecraft.class_2535$1").getDeclaredMethods())
.map(Method::toString)
.toArray(String[]::new)));
} catch (ClassNotFoundException ignored) {
}
}
return obj;
}
}

View File

@ -0,0 +1,34 @@
package com.viaversion.fabric.mc115.platform;
import com.viaversion.fabric.mc115.providers.VRHandItemProvider;
import com.viaversion.fabric.mc115.providers.VRVersionProvider;
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 VRLoader implements ViaPlatformLoader {
@Override
public void load() {
Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BungeeMovementTransmitter());
Via.getManager().getProviders().use(VersionProvider.class, new VRVersionProvider());
if (Via.getPlatform().getConf().isItemCache()) {
VRHandItemProvider handProvider = new VRHandItemProvider();
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
handProvider.registerClientTick();
}
handProvider.registerServerTick();
Via.getManager().getProviders().use(HandItemProvider.class, handProvider);
}
}
@Override
public void unload() {
// Nothing to do
}
}

View File

@ -0,0 +1,272 @@
package com.viaversion.fabric.mc115.platform;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.commands.NMSCommandSender;
import com.viaversion.fabric.mc115.commands.UserCommandSender;
import com.viaversion.fabric.mc115.util.FutureTaskId;
import com.viaversion.fabric.mc115.util.JLoggerToLog4j;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.platform.PlatformTask;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.network.MessageType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.apache.logging.log4j.LogManager;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaAPI;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.util.GsonUtil;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class VRPlatform implements ViaPlatform<UUID> {
private final Logger logger = new JLoggerToLog4j(LogManager.getLogger("ViaVersion"));
private final VRViaConfig config;
private final File dataFolder;
private final ViaAPI<UUID> api;
public VRPlatform() {
Path configDir = FabricLoader.getInstance().getConfigDirectory().toPath().resolve("ViaFabric");
config = new VRViaConfig(configDir.resolve("viaversion.yml").toFile());
dataFolder = configDir.toFile();
api = new VRViaAPI();
}
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();
}
public static String legacyToJson(String legacy) {
return GsonComponentSerializer.gson().serialize(LegacyComponentSerializer.legacySection().deserialize(legacy));
}
@Override
public Logger getLogger() {
return logger;
}
@Override
public String getPlatformName() {
return "ViaFabric";
}
@Override
public String getPlatformVersion() {
return ViaFabric.getVersion();
}
@Override
public String getPluginVersion() {
return FabricLoader.getInstance().getModContainer("viaversion").map(ModContainer::getMetadata)
.map(ModMetadata::getVersion).map(Version::getFriendlyString).orElse("UNKNOWN");
}
@Override
public FutureTaskId runAsync(Runnable runnable) {
return new FutureTaskId(CompletableFuture
.runAsync(runnable, ViaFabric.ASYNC_EXECUTOR)
.exceptionally(throwable -> {
if (!(throwable instanceof CancellationException)) {
throwable.printStackTrace();
}
return null;
})
);
}
@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()));
}
private FutureTaskId runEventLoop(Runnable runnable) {
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.submit(runnable)
.addListener(errorLogger())
);
}
@Override
public PlatformTask runSync(Runnable runnable, Long ticks) {
// ViaVersion seems to not need to run delayed tasks on main thread
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.schedule(() -> runSync(runnable), ticks * 50, TimeUnit.MILLISECONDS)
.addListener(errorLogger())
);
}
@Override
public PlatformTask runRepeatingSync(Runnable runnable, Long ticks) {
// ViaVersion seems to not need to run repeating tasks on main thread
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.scheduleAtFixedRate(() -> runSync(runnable), 0, ticks * 50, TimeUnit.MILLISECONDS)
.addListener(errorLogger())
);
}
private <T extends Future<?>> GenericFutureListener<T> errorLogger() {
return future -> {
if (!future.isCancelled() && future.cause() != null) {
future.cause().printStackTrace();
}
};
}
@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.sendChatMessage(Text.Serializer.fromJson(legacyToJson(s)), MessageType.SYSTEM);
});
}
@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(Text.Serializer.fromJson(legacyToJson(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
public boolean isPluginEnabled() {
return true;
}
@Override
public ViaAPI<UUID> getApi() {
return api;
}
@Override
public ViaVersionConfig getConf() {
return config;
}
@Override
public ConfigurationProvider getConfigurationProvider() {
return config;
}
@Override
public File getDataFolder() {
return dataFolder;
}
@Override
public void onReload() {
// Nothing to do
}
@Override
public JsonObject getDump() {
JsonObject platformSpecific = new JsonObject();
List<PluginInfo> mods = new ArrayList<>();
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
mods.add(new PluginInfo(true,
mod.getMetadata().getId() + " (" + mod.getMetadata().getName() + ")",
mod.getMetadata().getVersion().getFriendlyString(),
null,
mod.getMetadata().getAuthors().stream()
.map(info -> info.getName()
+ (info.getContact().asMap().isEmpty() ? "" : " " + info.getContact().asMap()))
.collect(Collectors.toList())
));
}
platformSpecific.add("mods", GsonUtil.getGson().toJsonTree(mods));
return platformSpecific;
}
@Override
public boolean isOldClientsAllowed() {
return true;
}
}

View File

@ -0,0 +1,8 @@
package com.viaversion.fabric.mc115.platform;
import com.viaversion.viaversion.ViaAPIBase;
import java.util.UUID;
public class VRViaAPI extends ViaAPIBase<UUID> {
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc115.platform;
import com.viaversion.viaversion.configuration.AbstractViaConfig;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class VRViaConfig extends AbstractViaConfig {
// Based on Sponge ViaVersion
private static List<String> UNSUPPORTED = Arrays.asList("anti-xray-patch", "bungee-ping-interval",
"bungee-ping-save", "bungee-servers", "quick-move-action-fix", "nms-player-ticking",
"velocity-ping-interval", "velocity-ping-save", "velocity-servers",
"blockconnection-method", "change-1_9-hitbox", "change-1_14-hitbox");
public VRViaConfig(File configFile) {
super(configFile);
// Load config
reloadConfig();
}
@Override
public URL getDefaultConfigURL() {
return getClass().getClassLoader().getResource("assets/viaversion/config.yml");
}
@Override
protected void handleConfig(Map<String, Object> config) {
// Nothing Currently
}
@Override
public List<String> getUnsupportedOptions() {
return UNSUPPORTED;
}
@Override
public boolean isAntiXRay() {
return false;
}
@Override
public boolean isNMSPlayerTicking() {
return false;
}
@Override
public boolean is1_12QuickMoveActionFix() {
return false;
}
@Override
public String getBlockConnectionMethod() {
return "packet";
}
@Override
public boolean is1_9HitboxFix() {
return false;
}
@Override
public boolean is1_14HitboxFix() {
return false;
}
}

View File

@ -0,0 +1,34 @@
package com.viaversion.fabric.mc115.protocol;
import com.viaversion.fabric.mc115.ViaFabricAddress;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.protocol.packet.State;
public class ViaFabricHostnameProtocol extends AbstractSimpleProtocol {
public static final ViaFabricHostnameProtocol INSTANCE = new ViaFabricHostnameProtocol();
@Override
protected void registerPackets() {
registerServerbound(State.HANDSHAKE, 0, 0, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // Protocol version
map(Type.STRING, new ValueTransformer<String, String>(Type.STRING) {
@Override
public String transform(PacketWrapper packetWrapper, String s) {
return new ViaFabricAddress().parse(s).realAddress;
}
});
}
});
}
@Override
public boolean isBaseProtocol() {
return true;
}
}

View File

@ -0,0 +1,110 @@
package com.viaversion.fabric.mc115.providers;
import com.viaversion.fabric.mc115.ViaFabric;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class VRHandItemProvider extends HandItemProvider {
public Item clientItem = null;
public Map<UUID, Item> serverPlayers = new ConcurrentHashMap<>();
@Override
public Item getHandItem(UserConnection info) {
Item serverItem;
if (info.isClientSide()) {
return getClientItem();
} else if ((serverItem = serverPlayers.get(info.getProtocolInfo().getUuid())) != null) {
return new Item(serverItem);
}
return super.getHandItem(info);
}
private Item getClientItem() {
if (clientItem == null) {
return new Item(0, (byte) 0, (short) 0, null);
}
return new Item(clientItem);
}
@Environment(EnvType.CLIENT)
public void registerClientTick() {
try {
ClientTickEvents.END_WORLD_TICK.register(clientWorld -> tickClient());
} catch (NoClassDefFoundError ignored) {
try {
WorldTickCallback.EVENT.register(world -> {
if (world.isClient) {
tickClient();
}
});
} catch (NoClassDefFoundError ignored2) {
ViaFabric.JLOGGER.info("Fabric Lifecycle V0/V1 isn't installed");
}
}
}
public void registerServerTick() {
try {
ServerTickEvents.END_WORLD_TICK.register(this::tickServer);
} catch (NoClassDefFoundError ignored) {
WorldTickCallback.EVENT.register(world -> {
if (!world.isClient) {
tickServer(world);
}
});
}
}
private void tickClient() {
ClientPlayerEntity p = MinecraftClient.getInstance().player;
if (p != null) {
clientItem = fromNative(p.inventory.getMainHandStack());
}
}
private void tickServer(World world) {
serverPlayers.clear();
world.getPlayers().forEach(it -> serverPlayers
.put(it.getUuid(), fromNative(it.inventory.getMainHandStack())));
}
private Item fromNative(ItemStack original) {
Identifier iid = Registry.ITEM.getId(original.getItem());
if (iid == null) return new Item(0, (byte) 0, (short) 0, null);
int id = swordId(iid.toString());
return new Item(id, (byte) original.getCount(), (short) original.getDamage(), null);
}
private int swordId(String id) {
// https://github.com/ViaVersion/ViaVersion/blob/8de26a0ad33f5b739f5394ed80f69d14197fddc7/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9To1_8.java#L86
switch (id) {
case "minecraft:iron_sword":
return 267;
case "minecraft:wooden_sword":
return 268;
case "minecraft:golden_sword":
return 272;
case "minecraft:diamond_sword":
return 276;
case "minecraft:stone_sword":
return 283;
}
return 0;
}
}

View File

@ -0,0 +1,166 @@
package com.viaversion.fabric.mc115.providers;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.ViaFabricAddress;
import com.viaversion.fabric.mc115.service.ProtocolAutoDetector;
import com.viaversion.fabric.mc115.util.ProtocolUtils;
import com.google.common.primitives.Ints;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.network.ClientConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.protocols.base.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.stream.IntStream;
public class VRVersionProvider extends BaseVersionProvider {
private int[] multiconnectSupportedVersions = null;
{
try {
if (FabricLoader.getInstance().isModLoaded("multiconnect")) {
Class<?> mcApiClass = Class.forName("net.earthcomputer.multiconnect.api.MultiConnectAPI");
Class<?> iProtocolClass = Class.forName("net.earthcomputer.multiconnect.api.IProtocol");
Object mcApiInstance = mcApiClass.getMethod("instance").invoke(null);
List<?> protocols = (List<?>) mcApiClass.getMethod("getSupportedProtocols").invoke(mcApiInstance);
Method getValue = iProtocolClass.getMethod("getValue");
Method isMulticonnectBeta;
try {
isMulticonnectBeta = iProtocolClass.getMethod("isMulticonnectBeta");
} catch (NoSuchMethodException e) {
isMulticonnectBeta = null;
}
Set<Integer> vers = new TreeSet<>();
for (Object protocol : protocols) {
// Do not use versions with beta multiconnect support, which may have stability issues
if (isMulticonnectBeta == null || !(Boolean) isMulticonnectBeta.invoke(protocol)) {
vers.add((Integer) getValue.invoke(protocol));
}
}
multiconnectSupportedVersions = vers.stream().mapToInt(Integer::intValue).toArray();
ViaFabric.JLOGGER.info("ViaFabric will integrate with multiconnect");
}
} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException
| ClassCastException ignored) {
}
}
@Override
public int getClosestServerProtocol(UserConnection connection) throws Exception {
if (connection.isClientSide()) {
ProtocolInfo info = Objects.requireNonNull(connection.getProtocolInfo());
if (!ViaFabric.config.isClientSideEnabled()) {
return info.getProtocolVersion();
}
int serverVer = ViaFabric.config.getClientSideVersion();
SocketAddress addr = connection.getChannel().remoteAddress();
if (addr instanceof InetSocketAddress) {
int addrVersion = new ViaFabricAddress().parse(((InetSocketAddress) addr).getHostName()).protocol;
if (addrVersion != 0) serverVer = addrVersion;
try {
if (serverVer == -2) {
// Hope protocol was autodetected
ProtocolVersion autoVer =
ProtocolAutoDetector.detectVersion((InetSocketAddress) addr).getNow(null);
if (autoVer != null) {
serverVer = autoVer.getVersion();
}
}
} catch (Exception e) {
ViaFabric.JLOGGER.warning("Couldn't auto detect: " + e);
}
}
boolean blocked = checkAddressBlocked(addr);
boolean supported = ProtocolUtils.isSupported(serverVer, info.getProtocolVersion());
handleMulticonnectPing(connection, info, blocked, serverVer);
if (blocked || !supported) serverVer = info.getProtocolVersion();
return serverVer;
}
return super.getClosestServerProtocol(connection);
}
private boolean checkAddressBlocked(SocketAddress addr) {
return addr instanceof InetSocketAddress && (isDisabled(((InetSocketAddress) addr).getHostString())
|| ((((InetSocketAddress) addr).getAddress() != null) &&
(isDisabled(((InetSocketAddress) addr).getAddress().getHostAddress())
|| isDisabled(((InetSocketAddress) addr).getAddress().getHostName()))));
}
private void handleMulticonnectPing(UserConnection connection, ProtocolInfo info, boolean blocked, int serverVer) throws Exception {
if (info.getState() == State.STATUS
&& info.getProtocolVersion() == -1
&& connection.getChannel().pipeline().get(ClientConnection.class).getPacketListener()
.getClass().getName().startsWith("net.earthcomputer.multiconnect")
&& (blocked || ProtocolUtils.isSupported(serverVer, getVersionForMulticonnect(serverVer)))) { // Intercept the connection
int multiconnectSuggestion = blocked ? -1 : getVersionForMulticonnect(serverVer);
ViaFabric.JLOGGER.info("Sending " + ProtocolVersion.getProtocol(multiconnectSuggestion) + " for multiconnect version detector");
PacketWrapper newAnswer = PacketWrapper.create(0x00, null, connection);
newAnswer.write(Type.STRING, "{\"version\":{\"name\":\"viafabric integration\",\"protocol\":" + multiconnectSuggestion + "}}");
newAnswer.send(info.getPipeline().contains(BaseProtocol1_16.class) ? BaseProtocol1_16.class : BaseProtocol1_7.class, true, true);
throw CancelException.generate();
}
}
private int getVersionForMulticonnect(int clientSideVersion) {
// https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java
int[] compatibleProtocols = multiconnectSupportedVersions;
if (Arrays.binarySearch(compatibleProtocols, clientSideVersion) >= 0) {
return clientSideVersion;
}
if (clientSideVersion < compatibleProtocols[0]) {
return compatibleProtocols[0];
}
// TODO: This needs a better fix, i.e checking ProtocolRegistry to see if it would work.
for (int i = compatibleProtocols.length - 1; i >= 0; i--) {
int protocol = compatibleProtocols[i];
if (clientSideVersion > protocol && ProtocolVersion.isRegistered(protocol)) {
return protocol;
}
}
ViaFabric.JLOGGER.severe("multiconnect integration: Panic, no protocol id found for " + clientSideVersion);
return clientSideVersion;
}
private boolean isDisabled(String addr) {
String[] parts = addr.split("\\.");
boolean isNumericIp = parts.length == 4 && Arrays.stream(parts).map(Ints::tryParse).allMatch(Objects::nonNull);
return IntStream.range(0, parts.length).anyMatch(i -> {
String query;
if (isNumericIp) {
query = String.join(".", Arrays.stream(parts, 0, i + 1)
.toArray(String[]::new)) + ((i != 3) ? ".*" : "");
} else {
query = ((i != 0) ? "*." : "") + String.join(".", Arrays.stream(parts, i, parts.length)
.toArray(String[]::new));
}
if (ViaFabric.config.isForcedDisable(query)) {
ViaFabric.JLOGGER.info(addr + " is force-disabled. (Matches " + query + ")");
return true;
} else {
return false;
}
});
}
}

View File

@ -0,0 +1,125 @@
package com.viaversion.fabric.mc115.service;
import com.viaversion.fabric.mc115.ViaFabric;
import com.viaversion.fabric.mc115.ViaFabricAddress;
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.LiteralText;
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 LoadingCache<InetSocketAddress, CompletableFuture<ProtocolVersion>> SERVER_VER = CacheBuilder.newBuilder()
.expireAfterAccess(100, 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<Channel>() {
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.getServerMetadata();
ServerMetadata.Version version;
if (meta != null && (version = meta.getVersion()) != null) {
ProtocolVersion ver = ProtocolVersion.getProtocol(version.getProtocolVersion());
future.complete(ver);
ViaFabric.JLOGGER.info("Auto-detected " + ver + " for " + address);
} else {
future.completeExceptionally(new IllegalArgumentException("Null version in query response"));
}
clientConnection.disconnect(new LiteralText(""));
}
@Override
public void onPong(QueryPongS2CPacket packet) {
clientConnection.disconnect(new LiteralText("Pong not requested!"));
}
@Override
public void onDisconnected(Text reason) {
future.completeExceptionally(new IllegalStateException(reason.asString()));
}
@Override
public ClientConnection getConnection() {
return clientConnection;
}
});
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<ProtocolVersion> detectVersion(InetSocketAddress address) {
try {
InetSocketAddress real = new InetSocketAddress(InetAddress.getByAddress
(new ViaFabricAddress().parse(address.getHostString()).realAddress,
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,23 @@
package com.viaversion.fabric.mc115.util;
import com.viaversion.viaversion.api.platform.PlatformTask;
import java.util.concurrent.Future;
public class FutureTaskId implements PlatformTask<Future<?>> {
private final Future<?> object;
public FutureTaskId(Future<?> object) {
this.object = object;
}
@Override
public Future<?> getObject() {
return object;
}
@Override
public void cancel() {
object.cancel(false);
}
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc115.util;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class JLoggerToLog4j extends Logger {
private final org.apache.logging.log4j.Logger base;
public JLoggerToLog4j(org.apache.logging.log4j.Logger logger) {
super("logger", null);
this.base = logger;
}
public void log(LogRecord record) {
this.log(record.getLevel(), record.getMessage());
}
public void log(Level level, String msg) {
if (level == Level.FINE) {
this.base.debug(msg);
} else if (level == Level.WARNING) {
this.base.warn(msg);
} else if (level == Level.SEVERE) {
this.base.error(msg);
} else if (level == Level.INFO) {
this.base.info(msg);
} else {
this.base.trace(msg);
}
}
public void log(Level level, String msg, Object param1) {
if (level == Level.FINE) {
this.base.debug(msg, param1);
} else if (level == Level.WARNING) {
this.base.warn(msg, param1);
} else if (level == Level.SEVERE) {
this.base.error(msg, param1);
} else if (level == Level.INFO) {
this.base.info(msg, param1);
} else {
this.base.trace(msg, param1);
}
}
public void log(Level level, String msg, Object[] params) {
log(level, MessageFormat.format(msg, params));
}
public void log(Level level, String msg, Throwable params) {
if (level == Level.FINE) {
this.base.debug(msg, params);
} else if (level == Level.WARNING) {
this.base.warn(msg, params);
} else if (level == Level.SEVERE) {
this.base.error(msg, params);
} else if (level == Level.INFO) {
this.base.info(msg, params);
} else {
this.base.trace(msg, params);
}
}
}

View File

@ -0,0 +1,60 @@
package com.viaversion.fabric.mc115.util;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.Arrays;
import java.util.stream.Stream;
public class ProtocolUtils {
public static boolean isSupported(int server, int client) {
return server == client || Via.getManager().getProtocolManager().getProtocolPath(client, server) != null;
}
public static String getProtocolName(int id) {
if (!ProtocolVersion.isRegistered(id)) return Integer.toString(id);
return ProtocolVersion.getProtocol(id).getName();
}
public static boolean isStartOfProtocolText(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
try {
Integer.parseInt(s + '0');
return true;
} catch (NumberFormatException e2) {
return ProtocolVersion.getProtocols().stream()
.map(ProtocolVersion::getName)
.flatMap(str -> Stream.concat(
Arrays.stream(str.split("-")),
Arrays.stream(new String[]{str})
))
.anyMatch(ver -> ver.startsWith(s));
}
}
}
public static Integer parseProtocolId(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ignored) {
ProtocolVersion closest = ProtocolVersion.getClosest(s);
if (closest == null) return null;
return closest.getVersion();
}
}
public static String[] getProtocolSuggestions(String text) {
return ProtocolVersion.getProtocols().stream()
.map(ProtocolVersion::getName)
.flatMap(str -> Stream.concat(
Arrays.stream(str.split("-")),
Arrays.stream(new String[]{str})
))
.distinct()
.filter(ver -> ver.startsWith(text))
.toArray(String[]::new);
}
}

View File

@ -0,0 +1,61 @@
{
"schemaVersion": 1,
"id": "viafabric-mc115",
"name": "ViaFabric",
"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.mc115.ViaFabric"
],
"cotton-client-commands": [
"com.viaversion.fabric.mc115.commands.VRClientCommands"
],
"modmenu": [
"com.viaversion.fabric.mc115.gui.ModMenuConfig"
]
},
"depends": {
"cotton-client-commands": "*",
"fabricloader": ">=0.4.0",
"fabric-resource-loader-v0": "*",
"minecraft": "1.15.x",
"viaversion": ">3.0.1"
},
"conflicts": {
"fabric-registry-sync-v0": "*"
},
"recommends": {
"fabric-command-api-v1": "*"
},
"icon": "assets/viafabric/textures/logo.png",
"mixins": [
"mixins.viafabric.address.json",
"mixins.viafabric.gui.json",
"mixins.viafabric.debug.json",
"mixins.viafabric.pipeline.json"
]
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc115.mixin.address.client.MixinConnectScreenThread",
"com.viaversion.fabric.mc115.mixin.address.client.MixinServerAddress",
"com.viaversion.fabric.mc115.mixin.address.client.MixinServerPinger"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc115.mixin.debug.client.MixinClientConnectionAccessor",
"com.viaversion.fabric.mc115.mixin.debug.client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc115.mixin.gui.client.MixinMultiplayerScreen"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
"com.viaversion.fabric.mc115.mixin.pipeline.MixinClientConnection",
"com.viaversion.fabric.mc115.mixin.pipeline.MixinServerNetworkIoChInit"
],
"client": [
"com.viaversion.fabric.mc115.mixin.pipeline.client.MixinClientConnectionChInit"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -1,11 +1,10 @@
version = rootProject.version
dependencies {
minecraft("com.mojang:minecraft:1.14.4")
mappings("net.fabricmc:yarn:1.14.4+build.16:v2")
modImplementation("net.fabricmc:fabric-loader:0.8.2+build.194")
minecraft("com.mojang:minecraft:1.16.5")
mappings("net.fabricmc:yarn:1.16.5+build.6:v2")
modImplementation("net.fabricmc:fabric-loader:0.11.3")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.13.1+build.257-1.14")
modImplementation("io.github.prospector:modmenu:1.7.16.1.14.4+build.128")
modImplementation("io.github.cottonmc:cotton-client-commands:1.0.0+1.15.2")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.32.5+1.16")
modImplementation("com.terraformersmc:modmenu:1.16.9")
}

View File

@ -0,0 +1,115 @@
package com.viaversion.fabric.mc116;
import com.viaversion.fabric.mc116.commands.VRCommandHandler;
import com.viaversion.fabric.mc116.config.VRConfig;
import com.viaversion.fabric.mc116.platform.VRInjector;
import com.viaversion.fabric.mc116.platform.VRLoader;
import com.viaversion.fabric.mc116.platform.VRPlatform;
import com.viaversion.fabric.mc116.protocol.ViaFabricHostnameProtocol;
import com.viaversion.fabric.mc116.util.JLoggerToLog4j;
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 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.v1.ClientCommandManager;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.command.CommandSource;
import org.apache.logging.log4j.LogManager;
import com.viaversion.viaversion.ViaManagerImpl;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
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 CompletableFuture<Void> INIT_FUTURE = new CompletableFuture<>();
public static VRConfig 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 String getVersion() {
return FabricLoader.getInstance().getModContainer("viafabric")
.get().getMetadata().getVersion().getFriendlyString();
}
public static <S extends CommandSource> LiteralArgumentBuilder<S> command(String commandName) {
return LiteralArgumentBuilder.<S>literal(commandName)
.then(
RequiredArgumentBuilder
.<S, String>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() {
Via.init(ViaManagerImpl.builder()
.injector(new VRInjector())
.loader(new VRLoader())
.commandHandler(new VRCommandHandler())
.platform(new VRPlatform()).build());
FabricLoader.getInstance().getModContainer("viabackwards").ifPresent(mod -> MappingDataLoader.enableMappingsCache());
((ViaManagerImpl) Via.getManager()).init();
Via.getManager().getProtocolManager().registerBaseProtocol(ViaFabricHostnameProtocol.INSTANCE, Range.lessThan(Integer.MIN_VALUE));
ProtocolVersion.register(-2, "AUTO");
FabricLoader.getInstance().getEntrypoints("viafabric:via_api_initialized", Runnable.class).forEach(Runnable::run);
try {
registerCommandsV1();
} catch (NoClassDefFoundError ignored) {
try {
registerCommandsV0();
JLOGGER.info("Using Fabric Commands V0");
} catch (NoClassDefFoundError ignored2) {
JLOGGER.info("Couldn't register command as Fabric Commands isn't installed");
}
}
config = new VRConfig(FabricLoader.getInstance().getConfigDir().resolve("ViaFabric")
.resolve("viafabric.yml").toFile());
INIT_FUTURE.complete(null);
}
private void registerCommandsV1() {
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("viaversion")));
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("viaver")));
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(command("vvfabric")));
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
ClientCommandManager.DISPATCHER.register(command("viafabricclient"));
}
}
@SuppressWarnings("deprecation")
private void registerCommandsV0() {
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("viaversion")));
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("viaver")));
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(command("vvfabric")));
}
}

View File

@ -0,0 +1,77 @@
package com.viaversion.fabric.mc116;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.Locale;
public class ViaFabricAddress {
public int protocol = 0;
public String viaSuffix = null;
public String realAddress = null;
public ViaFabricAddress parse(String address) {
if (address == null) return null;
String[] parts = address.split("\\.");
boolean foundDomain = false;
boolean foundOptions = false;
StringBuilder ourParts = new StringBuilder();
StringBuilder realAddrBuilder = new StringBuilder();
for (int i = parts.length - 1; i >= 0; i--) {
String part = parts[i];
boolean realAddrPart = false;
if (foundDomain) {
if (!foundOptions) {
if (part.startsWith("_")) {
String arg = part.substring(2);
if (part.toLowerCase(Locale.ROOT).startsWith("_v")) {
try {
protocol = Integer.parseInt(arg);
} catch (NumberFormatException e) {
ProtocolVersion closest = ProtocolVersion.getClosest(arg.replace("_", "."));
if (closest != null) {
protocol = closest.getVersion();
}
}
}
} else {
foundOptions = true;
}
}
if (foundOptions) {
realAddrPart = true;
}
} else if (part.equalsIgnoreCase("viafabric")) {
foundDomain = true;
}
if (realAddrPart) {
realAddrBuilder.insert(0, part + ".");
} else {
ourParts.insert(0, part + ".");
}
}
String realAddr = realAddrBuilder.toString().replaceAll("\\.$", "");
String suffix = ourParts.toString().replaceAll("\\.$", "");
if (realAddr.isEmpty()) {
this.realAddress = address;
} else {
this.realAddress = realAddr;
this.viaSuffix = suffix;
}
return this;
}
@Override
public String toString() {
return "ViaFabricAddress{" +
"protocol=" + protocol +
", viaSuffix='" + viaSuffix + '\'' +
", realAddress='" + realAddress + '\'' +
'}';
}
}

View File

@ -0,0 +1,61 @@
package com.viaversion.fabric.mc116.commands;
import com.viaversion.fabric.mc116.platform.VRPlatform;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
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);
}
@Override
public void sendMessage(String s) {
if (source instanceof ServerCommandSource) {
((ServerCommandSource) source).sendFeedback(Text.Serializer.fromJson(VRPlatform.legacyToJson(s)), false);
} else if (source instanceof FabricClientCommandSource) {
((FabricClientCommandSource) source).sendFeedback(Text.Serializer.fromJson(VRPlatform.legacyToJson(s)));
}
}
private String legacyToJson(String legacy) {
return GsonComponentSerializer.gson().serialize(LegacyComponentSerializer.legacySection().deserialize(legacy));
}
@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 "?";
}
}

View File

@ -0,0 +1,35 @@
package com.viaversion.fabric.mc116.commands;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.connection.UserConnection;
import java.util.UUID;
public class UserCommandSender implements ViaCommandSender {
private UserConnection con;
public UserCommandSender(UserConnection con) {
this.con = con;
}
@Override
public boolean hasPermission(String s) {
return false;
}
@Override
public void sendMessage(String s) {
Via.getPlatform().sendMessage(getUUID(), s);
}
@Override
public UUID getUUID() {
return con.getProtocolInfo().getUuid();
}
@Override
public String getName() {
return con.getProtocolInfo().getUsername();
}
}

View File

@ -0,0 +1,55 @@
package com.viaversion.fabric.mc116.commands;
import com.viaversion.fabric.mc116.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<? 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,48 @@
package com.viaversion.fabric.mc116.commands.subs;
import io.netty.util.ResourceLeakDetector;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LeakDetectSubCommand extends ViaSubCommand {
@Override
public String name() {
return "leakdetect";
}
@Override
public String description() {
return "Sets ResourceLeakDetector level";
}
@Override
public boolean execute(ViaCommandSender viaCommandSender, String[] strings) {
if (strings.length == 1) {
try {
ResourceLeakDetector.Level level = ResourceLeakDetector.Level.valueOf(strings[0]);
ResourceLeakDetector.setLevel(level);
viaCommandSender.sendMessage("Set leak detector level to " + level);
} catch (IllegalArgumentException e) {
viaCommandSender.sendMessage("Invalid level (" + Arrays.toString(ResourceLeakDetector.Level.values()) + ")");
}
} else {
viaCommandSender.sendMessage("Current leak detection level is " + ResourceLeakDetector.getLevel());
}
return true;
}
@Override
public List<String> onTabComplete(ViaCommandSender sender, String[] args) {
if (args.length == 1) {
return Arrays.stream(ResourceLeakDetector.Level.values())
.map(Enum::name)
.filter(it -> it.startsWith(args[0]))
.collect(Collectors.toList());
}
return super.onTabComplete(sender, args);
}
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc116.config;
import com.viaversion.viaversion.util.Config;
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class VRConfig extends Config {
public static final String ENABLE_CLIENT_SIDE = "enable-client-side";
public static final String CLIENT_SIDE_VERSION = "client-side-version";
public static final String CLIENT_SIDE_FORCE_DISABLE = "client-side-force-disable";
public static final String HIDE_BUTTON = "hide-button";
public VRConfig(File configFile) {
super(configFile);
reloadConfig();
}
@Override
public URL getDefaultConfigURL() {
return getClass().getClassLoader().getResource("assets/viafabric/config.yml");
}
@Override
protected void handleConfig(Map<String, Object> map) {
}
@Override
public List<String> getUnsupportedOptions() {
return Collections.emptyList();
}
public boolean isClientSideEnabled() {
return getBoolean(ENABLE_CLIENT_SIDE, false);
}
public void setClientSideEnabled(boolean val) {
set(ENABLE_CLIENT_SIDE, val);
}
public int getClientSideVersion() {
return getInt(CLIENT_SIDE_VERSION, -1);
}
public void setClientSideVersion(int val) {
set(CLIENT_SIDE_VERSION, val);
}
public Collection<?> getClientSideForceDisable() {
return (List<?>) get(CLIENT_SIDE_FORCE_DISABLE, List.class, Collections.emptyList());
}
public void setHideButton(boolean val) {
set(HIDE_BUTTON, val);
}
public boolean isHideButton() {
return getBoolean(HIDE_BUTTON, false);
}
public boolean isForcedDisable(String line) {
return getClientSideForceDisable().contains(line);
}
}

View File

@ -0,0 +1,11 @@
package com.viaversion.fabric.mc116.gui;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
public class ModMenuConfig implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return ViaConfigScreen::new;
}
}

View File

@ -0,0 +1,171 @@
package com.viaversion.fabric.mc116.gui;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.util.ProtocolUtils;
import com.viaversion.viaversion.api.Via;
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.screen.ScreenTexts;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.TranslatableText;
import java.util.concurrent.CompletableFuture;
@Environment(EnvType.CLIENT)
public class ViaConfigScreen extends Screen {
private static CompletableFuture<Void> latestProtocolSave;
private final Screen parent;
private TextFieldWidget protocolVersion;
public ViaConfigScreen(Screen parent) {
super(new TranslatableText("gui.viafabric_config.title"));
this.parent = parent;
}
private static int getProtocolTextColor(boolean valid, boolean supported) {
if (!valid) {
return 0xff0000; // Red
} else if (!supported) {
return 0xFFA500; // Orange
}
return 0xE0E0E0; // Default
}
@Override
protected void init() {
int entries = 0;
this.addButton(new ButtonWidget(this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, getClientSideText(), this::onClickClientSide));
entries++;
this.addButton(new ButtonWidget(this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, getHideViaButtonText(), this::onHideViaButton));
entries++;
protocolVersion = new TextFieldWidget(this.textRenderer,
this.width / 2 - 155 + entries % 2 * 160,
this.height / 6 + 24 * (entries >> 1),
150,
20, new TranslatableText("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.children.add(protocolVersion);
//noinspection ConstantConditions
if (entries % 2 == 1) {
entries++;
}
this.addButton(new ButtonWidget(this.width / 2 - 100, this.height / 6 + 24 * (entries >> 1), 200, 20, ScreenTexts.DONE, (buttonWidget) -> this.client.openScreen(this.parent)));
}
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(ProtocolUtils.isSupported(newVersion, Via.getAPI().getServerVersion().lowestSupportedVersion()),
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().openScreen(new ConfirmScreen(
answer -> {
if (answer) {
ViaFabric.config.setClientSideEnabled(true);
ViaFabric.config.setClientSideVersion(-2); // AUTO
ViaFabric.config.saveConfig();
widget.setMessage(getClientSideText());
}
MinecraftClient.getInstance().openScreen(this);
},
new TranslatableText("gui.enable_client_side.question"),
new TranslatableText("gui.enable_client_side.warning"),
new TranslatableText("gui.enable_client_side.enable"),
new TranslatableText("gui.cancel")
));
} else {
ViaFabric.config.setClientSideEnabled(false);
ViaFabric.config.saveConfig();
}
widget.setMessage(getClientSideText());
}
@Override
public void removed() {
ViaFabric.config.saveConfig();
}
@Override
public void onClose() {
this.client.openScreen(this.parent);
}
private TranslatableText getClientSideText() {
return ViaFabric.config.isClientSideEnabled() ?
new TranslatableText("gui.client_side.disable")
: new TranslatableText("gui.client_side.enable");
}
private TranslatableText getHideViaButtonText() {
return ViaFabric.config.isHideButton() ?
new TranslatableText("gui.hide_via_button.disable") : new TranslatableText("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);
drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 20, 16777215);
super.render(matrices, mouseX, mouseY, delta);
protocolVersion.render(matrices, mouseX, mouseY, delta);
}
@Override
public void tick() {
super.tick();
protocolVersion.tick();
}
}

View File

@ -0,0 +1,38 @@
package com.viaversion.fabric.mc116.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
public class CommonTransformer {
public static final String HANDLER_DECODER_NAME = "via-decoder";
public static final String HANDLER_ENCODER_NAME = "via-encoder";
public static void decompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
ChannelHandler handler = ctx.pipeline().get("decompress");
ByteBuf decompressed = handler instanceof MessageToMessageDecoder
? (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) handler, ctx, buf).get(0)
: (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) handler, ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
}
public static void compress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compress"), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
}

View File

@ -0,0 +1,85 @@
package com.viaversion.fabric.mc116.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelDecoderException;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@ChannelHandler.Sharable
public class FabricDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection info;
private boolean handledCompression;
private boolean skipDoubleTransform;
public FabricDecodeHandler(UserConnection info) {
this.info = info;
}
public UserConnection getInfo() {
return info;
}
// https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (skipDoubleTransform) {
skipDoubleTransform = false;
out.add(bytebuf.retain());
return;
}
if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformIncoming(transformedBuf, CancelDecoderException::generate);
if (needsCompress) {
CommonTransformer.compress(ctx, transformedBuf);
skipDoubleTransform = true;
}
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int decoderIndex = ctx.pipeline().names().indexOf("decompress");
if (decoderIndex == -1) return false;
handledCompression = true;
if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) {
// Need to decompress this packet due to bad order
CommonTransformer.decompress(ctx, buf);
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compress", "via-encoder", encoder);
ctx.pipeline().addAfter("decompress", "via-decoder", decoder);
return true;
}
return false;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return;
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,72 @@
package com.viaversion.fabric.mc116.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelEncoderException;
import com.viaversion.viaversion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@ChannelHandler.Sharable
public class FabricEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
private final UserConnection info;
private boolean handledCompression;
public FabricEncodeHandler(UserConnection info) {
this.info = info;
}
@Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformOutgoing(transformedBuf, CancelEncoderException::generate);
if (needsCompress) {
CommonTransformer.compress(ctx, transformedBuf);
}
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int encoderIndex = ctx.pipeline().names().indexOf("compress");
if (encoderIndex == -1) return false;
handledCompression = true;
if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order
CommonTransformer.decompress(ctx, buf);
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compress", "via-encoder", encoder);
ctx.pipeline().addAfter("decompress", "via-decoder", decoder);
return true;
}
return false;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return;
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,79 @@
package com.viaversion.fabric.mc116.handler.clientside;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.service.ProtocolAutoDetector;
import com.viaversion.viaversion.util.Pair;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import java.net.InetSocketAddress;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ProtocolDetectionHandler extends ChannelDuplexHandler {
private final Queue<Pair<Object, ChannelPromise>> queuedMessages = new ArrayDeque<>();
private boolean hold = true;
private boolean pendentFlush;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
if (ctx.channel().remoteAddress() instanceof InetSocketAddress) {
ScheduledFuture<?> timeoutRun = ctx.executor().schedule(() -> {
ViaFabric.JLOGGER.warning("Timeout for protocol auto-detection in "
+ ctx.channel().remoteAddress() + " server");
hold = false;
drainQueue(ctx);
ctx.pipeline().remove(this);
}, 10, TimeUnit.SECONDS);
ProtocolAutoDetector.detectVersion(((InetSocketAddress) ctx.channel().remoteAddress()))
.whenComplete((obj, ex) -> {
ctx.pipeline().remove(this);
timeoutRun.cancel(false);
});
// Let's cache it before we need it
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!hold) {
drainQueue(ctx);
super.write(ctx, msg, promise);
} else {
queuedMessages.add(new Pair<>(msg, promise));
}
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
if (!hold) {
drainQueue(ctx);
super.flush(ctx);
} else {
pendentFlush = true;
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
drainQueue(ctx);
super.channelInactive(ctx);
}
private void drainQueue(ChannelHandlerContext ctx) {
queuedMessages.forEach(it -> ctx.write(it.getKey(), it.getValue()));
queuedMessages.clear();
if (pendentFlush) ctx.flush();
pendentFlush = false;
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
drainQueue(ctx);
super.handlerRemoved(ctx);
}
}

View File

@ -0,0 +1,24 @@
package com.viaversion.fabric.mc116.mixin.address.client;
import com.viaversion.fabric.mc116.ViaFabricAddress;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Mixin(targets = "net/minecraft/client/gui/screen/ConnectScreen$1", priority = 2000)
public class MixinConnectScreenThread {
@Redirect(method = "run()V", at = @At(value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"))
private InetAddress resolveViaFabricAddr(String address) throws UnknownHostException {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return InetAddress.getByName(address);
}
InetAddress resolved = InetAddress.getByName(viaAddr.realAddress);
return InetAddress.getByAddress(resolved.getHostName() + "." + viaAddr.viaSuffix, resolved.getAddress());
}
}

View File

@ -0,0 +1,27 @@
package com.viaversion.fabric.mc116.mixin.address.client;
import com.viaversion.fabric.mc116.ViaFabricAddress;
import com.mojang.datafixers.util.Pair;
import net.minecraft.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.Redirect;
@Mixin(ServerAddress.class)
public abstract class MixinServerAddress {
@Shadow
private static Pair<String, Integer> resolveServer(String address) {
throw new AssertionError();
}
@Redirect(method = "parse", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ServerAddress;resolveServer(Ljava/lang/String;)Lcom/mojang/datafixers/util/Pair;"))
private static Pair<String, Integer> modifySrvAddr(String address) {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return resolveServer(address);
}
return resolveServer(viaAddr.realAddress).mapFirst(it -> it.replaceAll("\\.$", "") + "." + viaAddr.viaSuffix);
}
}

View File

@ -0,0 +1,25 @@
package com.viaversion.fabric.mc116.mixin.address.client;
import com.viaversion.fabric.mc116.ViaFabricAddress;
import net.minecraft.client.network.MultiplayerServerListPinger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Mixin(MultiplayerServerListPinger.class)
public class MixinServerPinger {
@Redirect(method = "add", at = @At(value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"))
private InetAddress resolveViaFabricAddr(String address) throws UnknownHostException {
ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address);
if (viaAddr.viaSuffix == null) {
return InetAddress.getByName(address);
}
InetAddress resolved = InetAddress.getByName(viaAddr.realAddress);
return InetAddress.getByAddress(resolved.getHostName() + "." + viaAddr.viaSuffix, resolved.getAddress());
}
}

View File

@ -0,0 +1,12 @@
package com.viaversion.fabric.mc116.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,38 @@
package com.viaversion.fabric.mc116.mixin.debug.client;
import com.viaversion.fabric.mc116.handler.CommonTransformer;
import com.viaversion.fabric.mc116.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<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) {
ProtocolInfo protocol = ((FabricDecodeHandler) viaDecoder).getInfo().getProtocolInfo();
if (protocol != null) {
ProtocolVersion serverVer = ProtocolVersion.getProtocol(protocol.getServerProtocolVersion());
ProtocolVersion clientVer = ProtocolVersion.getProtocol(protocol.getProtocolVersion());
line += " / C: " + clientVer.getName() + " (" + clientVer.getVersion() + ") S: "
+ serverVer.getName() + " (" + serverVer.getVersion() + ") A: " + protocol.getUser().isActive();
}
}
info.getReturnValue().add(line);
}
}

View File

@ -0,0 +1,38 @@
package com.viaversion.fabric.mc116.mixin.gui.client;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.gui.ViaConfigScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
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.text.TranslatableText;
import net.minecraft.util.Identifier;
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(MultiplayerScreen.class)
public abstract class MixinMultiplayerScreen extends Screen {
protected MixinMultiplayerScreen(Text title, UnsupportedOperationException e) {
super(title);
throw e;
}
@Inject(method = "init", at = @At("TAIL"))
private void onInit(CallbackInfo ci) {
ButtonWidget enableClientSideViaVersion = new TexturedButtonWidget(this.width / 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().openScreen(new ViaConfigScreen(this)),
new TranslatableText("gui.via_button"));
if (ViaFabric.config.isHideButton()) enableClientSideViaVersion.visible = false;
addButton(enableClientSideViaVersion);
}
}

View File

@ -0,0 +1,31 @@
package com.viaversion.fabric.mc116.mixin.pipeline;
import io.netty.channel.Channel;
import net.minecraft.network.ClientConnection;
import org.apache.logging.log4j.Logger;
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;
@Mixin(ClientConnection.class)
public class MixinClientConnection {
@Shadow
private Channel channel;
@Redirect(
method = "exceptionCaught",
remap = false,
at = @At(
value = "INVOKE",
target = "Lorg/apache/logging/log4j/Logger;debug(Ljava/lang/String;Ljava/lang/Throwable;)V"
))
private void redirectDebug(Logger logger, String message, Throwable t) {
if ("Failed to sent packet".equals(message)) {
logger.info(message, t);
} else {
logger.debug(message, t);
}
}
}

View File

@ -0,0 +1,28 @@
package com.viaversion.fabric.mc116.mixin.pipeline;
import com.viaversion.fabric.mc116.handler.CommonTransformer;
import com.viaversion.fabric.mc116.handler.FabricDecodeHandler;
import com.viaversion.fabric.mc116.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));
}
}
}

View File

@ -0,0 +1,35 @@
package com.viaversion.fabric.mc116.mixin.pipeline.client;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.handler.CommonTransformer;
import com.viaversion.fabric.mc116.handler.FabricDecodeHandler;
import com.viaversion.fabric.mc116.handler.FabricEncodeHandler;
import com.viaversion.fabric.mc116.handler.clientside.ProtocolDetectionHandler;
import com.viaversion.fabric.mc116.protocol.ViaFabricHostnameProtocol;
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(ViaFabricHostnameProtocol.INSTANCE);
channel.pipeline()
.addBefore("encoder", CommonTransformer.HANDLER_ENCODER_NAME, new FabricEncodeHandler(user))
.addBefore("decoder", CommonTransformer.HANDLER_DECODER_NAME, new FabricDecodeHandler(user));
if (ViaFabric.config.isClientSideEnabled()) {
channel.pipeline().addAfter(CommonTransformer.HANDLER_ENCODER_NAME, "via-autoprotocol", new ProtocolDetectionHandler());
}
}
}
}

View File

@ -0,0 +1,61 @@
package com.viaversion.fabric.mc116.platform;
import com.viaversion.fabric.mc116.handler.CommonTransformer;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.util.GsonUtil;
import com.viaversion.viaversion.libs.gson.JsonObject;
import java.lang.reflect.Method;
import java.util.Arrays;
public class VRInjector implements ViaInjector {
@Override
public void inject() {
// *looks at Mixins*
}
@Override
public void uninject() {
// not possible *plays sad violin*
}
@Override
public int getServerProtocolVersion() {
return SharedConstants.getGameVersion().getProtocolVersion();
}
@Override
public String getEncoderName() {
return CommonTransformer.HANDLER_ENCODER_NAME;
}
@Override
public String getDecoderName() {
return CommonTransformer.HANDLER_DECODER_NAME;
}
@Override
public JsonObject getDump() {
JsonObject obj = new JsonObject();
try {
obj.add("serverNetworkIOChInit", GsonUtil.getGson().toJsonTree(
Arrays.stream(Class.forName("net.minecraft.class_3242$1").getDeclaredMethods())
.map(Method::toString)
.toArray(String[]::new)));
} catch (ClassNotFoundException ignored) {
}
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
try {
obj.add("clientConnectionChInit", GsonUtil.getGson().toJsonTree(
Arrays.stream(Class.forName("net.minecraft.class_2535$1").getDeclaredMethods())
.map(Method::toString)
.toArray(String[]::new)));
} catch (ClassNotFoundException ignored) {
}
}
return obj;
}
}

View File

@ -0,0 +1,34 @@
package com.viaversion.fabric.mc116.platform;
import com.viaversion.fabric.mc116.providers.VRHandItemProvider;
import com.viaversion.fabric.mc116.providers.VRVersionProvider;
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 VRLoader implements ViaPlatformLoader {
@Override
public void load() {
Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BungeeMovementTransmitter());
Via.getManager().getProviders().use(VersionProvider.class, new VRVersionProvider());
if (Via.getPlatform().getConf().isItemCache()) {
VRHandItemProvider handProvider = new VRHandItemProvider();
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
handProvider.registerClientTick();
}
handProvider.registerServerTick();
Via.getManager().getProviders().use(HandItemProvider.class, handProvider);
}
}
@Override
public void unload() {
// Nothing to do
}
}

View File

@ -0,0 +1,273 @@
package com.viaversion.fabric.mc116.platform;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.commands.NMSCommandSender;
import com.viaversion.fabric.mc116.commands.UserCommandSender;
import com.viaversion.fabric.mc116.util.FutureTaskId;
import com.viaversion.fabric.mc116.util.JLoggerToLog4j;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.platform.PlatformTask;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.apache.logging.log4j.LogManager;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaAPI;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.util.GsonUtil;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class VRPlatform implements ViaPlatform<UUID> {
private final Logger logger = new JLoggerToLog4j(LogManager.getLogger("ViaVersion"));
private final VRViaConfig config;
private final File dataFolder;
private final ViaAPI<UUID> api;
public VRPlatform() {
Path configDir = FabricLoader.getInstance().getConfigDirectory().toPath().resolve("ViaFabric");
config = new VRViaConfig(configDir.resolve("viaversion.yml").toFile());
dataFolder = configDir.toFile();
api = new VRViaAPI();
}
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();
}
public static String legacyToJson(String legacy) {
return GsonComponentSerializer.gson().serialize(LegacyComponentSerializer.legacySection().deserialize(legacy));
}
@Override
public Logger getLogger() {
return logger;
}
@Override
public String getPlatformName() {
return "ViaFabric";
}
@Override
public String getPlatformVersion() {
return ViaFabric.getVersion();
}
@Override
public String getPluginVersion() {
return FabricLoader.getInstance().getModContainer("viaversion").map(ModContainer::getMetadata)
.map(ModMetadata::getVersion).map(Version::getFriendlyString).orElse("UNKNOWN");
}
@Override
public FutureTaskId runAsync(Runnable runnable) {
return new FutureTaskId(CompletableFuture
.runAsync(runnable, ViaFabric.ASYNC_EXECUTOR)
.exceptionally(throwable -> {
if (!(throwable instanceof CancellationException)) {
throwable.printStackTrace();
}
return null;
})
);
}
@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()));
}
private FutureTaskId runEventLoop(Runnable runnable) {
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.submit(runnable)
.addListener(errorLogger())
);
}
@Override
public PlatformTask runSync(Runnable runnable, Long ticks) {
// ViaVersion seems to not need to run delayed tasks on main thread
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.schedule(() -> runSync(runnable), ticks * 50, TimeUnit.MILLISECONDS)
.addListener(errorLogger())
);
}
@Override
public PlatformTask runRepeatingSync(Runnable runnable, Long ticks) {
// ViaVersion seems to not need to run repeating tasks on main thread
return new FutureTaskId(
ViaFabric.EVENT_LOOP
.scheduleAtFixedRate(() -> runSync(runnable), 0, ticks * 50, TimeUnit.MILLISECONDS)
.addListener(errorLogger())
);
}
private <T extends Future<?>> GenericFutureListener<T> errorLogger() {
return future -> {
if (!future.isCancelled() && future.cause() != null) {
future.cause().printStackTrace();
}
};
}
@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(Text.Serializer.fromJson(
legacyToJson(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(Text.Serializer.fromJson(legacyToJson(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
public boolean isPluginEnabled() {
return true;
}
@Override
public ViaAPI<UUID> getApi() {
return api;
}
@Override
public ViaVersionConfig getConf() {
return config;
}
@Override
public ConfigurationProvider getConfigurationProvider() {
return config;
}
@Override
public File getDataFolder() {
return dataFolder;
}
@Override
public void onReload() {
// Nothing to do
}
@Override
public JsonObject getDump() {
JsonObject platformSpecific = new JsonObject();
List<PluginInfo> mods = new ArrayList<>();
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
mods.add(new PluginInfo(true,
mod.getMetadata().getId() + " (" + mod.getMetadata().getName() + ")",
mod.getMetadata().getVersion().getFriendlyString(),
null,
mod.getMetadata().getAuthors().stream()
.map(info -> info.getName()
+ (info.getContact().asMap().isEmpty() ? "" : " " + info.getContact().asMap()))
.collect(Collectors.toList())
));
}
platformSpecific.add("mods", GsonUtil.getGson().toJsonTree(mods));
return platformSpecific;
}
@Override
public boolean isOldClientsAllowed() {
return true;
}
}

View File

@ -0,0 +1,8 @@
package com.viaversion.fabric.mc116.platform;
import com.viaversion.viaversion.ViaAPIBase;
import java.util.UUID;
public class VRViaAPI extends ViaAPIBase<UUID> {
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc116.platform;
import com.viaversion.viaversion.configuration.AbstractViaConfig;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class VRViaConfig extends AbstractViaConfig {
// Based on Sponge ViaVersion
private static List<String> UNSUPPORTED = Arrays.asList("anti-xray-patch", "bungee-ping-interval",
"bungee-ping-save", "bungee-servers", "quick-move-action-fix", "nms-player-ticking",
"velocity-ping-interval", "velocity-ping-save", "velocity-servers",
"blockconnection-method", "change-1_9-hitbox", "change-1_14-hitbox");
public VRViaConfig(File configFile) {
super(configFile);
// Load config
reloadConfig();
}
@Override
public URL getDefaultConfigURL() {
return getClass().getClassLoader().getResource("assets/viaversion/config.yml");
}
@Override
protected void handleConfig(Map<String, Object> config) {
// Nothing Currently
}
@Override
public List<String> getUnsupportedOptions() {
return UNSUPPORTED;
}
@Override
public boolean isAntiXRay() {
return false;
}
@Override
public boolean isNMSPlayerTicking() {
return false;
}
@Override
public boolean is1_12QuickMoveActionFix() {
return false;
}
@Override
public String getBlockConnectionMethod() {
return "packet";
}
@Override
public boolean is1_9HitboxFix() {
return false;
}
@Override
public boolean is1_14HitboxFix() {
return false;
}
}

View File

@ -0,0 +1,34 @@
package com.viaversion.fabric.mc116.protocol;
import com.viaversion.fabric.mc116.ViaFabricAddress;
import com.viaversion.viaversion.api.protocol.AbstractSimpleProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.protocol.packet.State;
public class ViaFabricHostnameProtocol extends AbstractSimpleProtocol {
public static final ViaFabricHostnameProtocol INSTANCE = new ViaFabricHostnameProtocol();
@Override
protected void registerPackets() {
registerServerbound(State.HANDSHAKE, 0, 0, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // Protocol version
map(Type.STRING, new ValueTransformer<String, String>(Type.STRING) {
@Override
public String transform(PacketWrapper packetWrapper, String s) {
return new ViaFabricAddress().parse(s).realAddress;
}
});
}
});
}
@Override
public boolean isBaseProtocol() {
return true;
}
}

View File

@ -0,0 +1,110 @@
package com.viaversion.fabric.mc116.providers;
import com.viaversion.fabric.mc116.ViaFabric;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class VRHandItemProvider extends HandItemProvider {
public Item clientItem = null;
public Map<UUID, Item> serverPlayers = new ConcurrentHashMap<>();
@Override
public Item getHandItem(UserConnection info) {
Item serverItem;
if (info.isClientSide()) {
return getClientItem();
} else if ((serverItem = serverPlayers.get(info.getProtocolInfo().getUuid())) != null) {
return new Item(serverItem);
}
return super.getHandItem(info);
}
private Item getClientItem() {
if (clientItem == null) {
return new Item(0, (byte) 0, (short) 0, null);
}
return new Item(clientItem);
}
@Environment(EnvType.CLIENT)
public void registerClientTick() {
try {
ClientTickEvents.END_WORLD_TICK.register(clientWorld -> tickClient());
} catch (NoClassDefFoundError ignored) {
try {
WorldTickCallback.EVENT.register(world -> {
if (world.isClient) {
tickClient();
}
});
} catch (NoClassDefFoundError ignored2) {
ViaFabric.JLOGGER.info("Fabric Lifecycle V0/V1 isn't installed");
}
}
}
public void registerServerTick() {
try {
ServerTickEvents.END_WORLD_TICK.register(this::tickServer);
} catch (NoClassDefFoundError ignored) {
WorldTickCallback.EVENT.register(world -> {
if (!world.isClient) {
tickServer(world);
}
});
}
}
private void tickClient() {
ClientPlayerEntity p = MinecraftClient.getInstance().player;
if (p != null) {
clientItem = fromNative(p.inventory.getMainHandStack());
}
}
private void tickServer(World world) {
serverPlayers.clear();
world.getPlayers().forEach(it -> serverPlayers
.put(it.getUuid(), fromNative(it.inventory.getMainHandStack())));
}
private Item fromNative(ItemStack original) {
Identifier iid = Registry.ITEM.getId(original.getItem());
if (iid == null) return new Item(0, (byte) 0, (short) 0, null);
int id = swordId(iid.toString());
return new Item(id, (byte) original.getCount(), (short) original.getDamage(), null);
}
private int swordId(String id) {
// https://github.com/ViaVersion/ViaVersion/blob/8de26a0ad33f5b739f5394ed80f69d14197fddc7/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9To1_8.java#L86
switch (id) {
case "minecraft:iron_sword":
return 267;
case "minecraft:wooden_sword":
return 268;
case "minecraft:golden_sword":
return 272;
case "minecraft:diamond_sword":
return 276;
case "minecraft:stone_sword":
return 283;
}
return 0;
}
}

View File

@ -0,0 +1,166 @@
package com.viaversion.fabric.mc116.providers;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.ViaFabricAddress;
import com.viaversion.fabric.mc116.service.ProtocolAutoDetector;
import com.viaversion.fabric.mc116.util.ProtocolUtils;
import com.google.common.primitives.Ints;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.network.ClientConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.protocols.base.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.stream.IntStream;
public class VRVersionProvider extends BaseVersionProvider {
private int[] multiconnectSupportedVersions = null;
{
try {
if (FabricLoader.getInstance().isModLoaded("multiconnect")) {
Class<?> mcApiClass = Class.forName("net.earthcomputer.multiconnect.api.MultiConnectAPI");
Class<?> iProtocolClass = Class.forName("net.earthcomputer.multiconnect.api.IProtocol");
Object mcApiInstance = mcApiClass.getMethod("instance").invoke(null);
List<?> protocols = (List<?>) mcApiClass.getMethod("getSupportedProtocols").invoke(mcApiInstance);
Method getValue = iProtocolClass.getMethod("getValue");
Method isMulticonnectBeta;
try {
isMulticonnectBeta = iProtocolClass.getMethod("isMulticonnectBeta");
} catch (NoSuchMethodException e) {
isMulticonnectBeta = null;
}
Set<Integer> vers = new TreeSet<>();
for (Object protocol : protocols) {
// Do not use versions with beta multiconnect support, which may have stability issues
if (isMulticonnectBeta == null || !(Boolean) isMulticonnectBeta.invoke(protocol)) {
vers.add((Integer) getValue.invoke(protocol));
}
}
multiconnectSupportedVersions = vers.stream().mapToInt(Integer::intValue).toArray();
ViaFabric.JLOGGER.info("ViaFabric will integrate with multiconnect");
}
} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException
| ClassCastException ignored) {
}
}
@Override
public int getClosestServerProtocol(UserConnection connection) throws Exception {
if (connection.isClientSide()) {
ProtocolInfo info = Objects.requireNonNull(connection.getProtocolInfo());
if (!ViaFabric.config.isClientSideEnabled()) {
return info.getProtocolVersion();
}
int serverVer = ViaFabric.config.getClientSideVersion();
SocketAddress addr = connection.getChannel().remoteAddress();
if (addr instanceof InetSocketAddress) {
int addrVersion = new ViaFabricAddress().parse(((InetSocketAddress) addr).getHostName()).protocol;
if (addrVersion != 0) serverVer = addrVersion;
try {
if (serverVer == -2) {
// Hope protocol was autodetected
ProtocolVersion autoVer =
ProtocolAutoDetector.detectVersion((InetSocketAddress) addr).getNow(null);
if (autoVer != null) {
serverVer = autoVer.getVersion();
}
}
} catch (Exception e) {
ViaFabric.JLOGGER.warning("Couldn't auto detect: " + e);
}
}
boolean blocked = checkAddressBlocked(addr);
boolean supported = ProtocolUtils.isSupported(serverVer, info.getProtocolVersion());
handleMulticonnectPing(connection, info, blocked, serverVer);
if (blocked || !supported) serverVer = info.getProtocolVersion();
return serverVer;
}
return super.getClosestServerProtocol(connection);
}
private boolean checkAddressBlocked(SocketAddress addr) {
return addr instanceof InetSocketAddress && (isDisabled(((InetSocketAddress) addr).getHostString())
|| ((((InetSocketAddress) addr).getAddress() != null) &&
(isDisabled(((InetSocketAddress) addr).getAddress().getHostAddress())
|| isDisabled(((InetSocketAddress) addr).getAddress().getHostName()))));
}
private void handleMulticonnectPing(UserConnection connection, ProtocolInfo info, boolean blocked, int serverVer) throws Exception {
if (info.getState() == State.STATUS
&& info.getProtocolVersion() == -1
&& connection.getChannel().pipeline().get(ClientConnection.class).getPacketListener()
.getClass().getName().startsWith("net.earthcomputer.multiconnect")
&& (blocked || ProtocolUtils.isSupported(serverVer, getVersionForMulticonnect(serverVer)))) { // Intercept the connection
int multiconnectSuggestion = blocked ? -1 : getVersionForMulticonnect(serverVer);
ViaFabric.JLOGGER.info("Sending " + ProtocolVersion.getProtocol(multiconnectSuggestion) + " for multiconnect version detector");
PacketWrapper newAnswer = PacketWrapper.create(0x00, null, connection);
newAnswer.write(Type.STRING, "{\"version\":{\"name\":\"viafabric integration\",\"protocol\":" + multiconnectSuggestion + "}}");
newAnswer.send(info.getPipeline().contains(BaseProtocol1_16.class) ? BaseProtocol1_16.class : BaseProtocol1_7.class, true, true);
throw CancelException.generate();
}
}
private int getVersionForMulticonnect(int clientSideVersion) {
// https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java
int[] compatibleProtocols = multiconnectSupportedVersions;
if (Arrays.binarySearch(compatibleProtocols, clientSideVersion) >= 0) {
return clientSideVersion;
}
if (clientSideVersion < compatibleProtocols[0]) {
return compatibleProtocols[0];
}
// TODO: This needs a better fix, i.e checking ProtocolRegistry to see if it would work.
for (int i = compatibleProtocols.length - 1; i >= 0; i--) {
int protocol = compatibleProtocols[i];
if (clientSideVersion > protocol && ProtocolVersion.isRegistered(protocol)) {
return protocol;
}
}
ViaFabric.JLOGGER.severe("multiconnect integration: Panic, no protocol id found for " + clientSideVersion);
return clientSideVersion;
}
private boolean isDisabled(String addr) {
String[] parts = addr.split("\\.");
boolean isNumericIp = parts.length == 4 && Arrays.stream(parts).map(Ints::tryParse).allMatch(Objects::nonNull);
return IntStream.range(0, parts.length).anyMatch(i -> {
String query;
if (isNumericIp) {
query = String.join(".", Arrays.stream(parts, 0, i + 1)
.toArray(String[]::new)) + ((i != 3) ? ".*" : "");
} else {
query = ((i != 0) ? "*." : "") + String.join(".", Arrays.stream(parts, i, parts.length)
.toArray(String[]::new));
}
if (ViaFabric.config.isForcedDisable(query)) {
ViaFabric.JLOGGER.info(addr + " is force-disabled. (Matches " + query + ")");
return true;
} else {
return false;
}
});
}
}

View File

@ -0,0 +1,125 @@
package com.viaversion.fabric.mc116.service;
import com.viaversion.fabric.mc116.ViaFabric;
import com.viaversion.fabric.mc116.ViaFabricAddress;
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.LiteralText;
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 LoadingCache<InetSocketAddress, CompletableFuture<ProtocolVersion>> SERVER_VER = CacheBuilder.newBuilder()
.expireAfterAccess(100, 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<Channel>() {
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.getServerMetadata();
ServerMetadata.Version version;
if (meta != null && (version = meta.getVersion()) != null) {
ProtocolVersion ver = ProtocolVersion.getProtocol(version.getProtocolVersion());
future.complete(ver);
ViaFabric.JLOGGER.info("Auto-detected " + ver + " for " + address);
} else {
future.completeExceptionally(new IllegalArgumentException("Null version in query response"));
}
clientConnection.disconnect(LiteralText.EMPTY);
}
@Override
public void onPong(QueryPongS2CPacket packet) {
clientConnection.disconnect(new LiteralText("Pong not requested!"));
}
@Override
public void onDisconnected(Text reason) {
future.completeExceptionally(new IllegalStateException(reason.asString()));
}
@Override
public ClientConnection getConnection() {
return clientConnection;
}
});
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<ProtocolVersion> detectVersion(InetSocketAddress address) {
try {
InetSocketAddress real = new InetSocketAddress(InetAddress.getByAddress
(new ViaFabricAddress().parse(address.getHostString()).realAddress,
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,23 @@
package com.viaversion.fabric.mc116.util;
import com.viaversion.viaversion.api.platform.PlatformTask;
import java.util.concurrent.Future;
public class FutureTaskId implements PlatformTask<Future<?>> {
private final Future<?> object;
public FutureTaskId(Future<?> object) {
this.object = object;
}
@Override
public Future<?> getObject() {
return object;
}
@Override
public void cancel() {
object.cancel(false);
}
}

View File

@ -0,0 +1,68 @@
package com.viaversion.fabric.mc116.util;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class JLoggerToLog4j extends Logger {
private final org.apache.logging.log4j.Logger base;
public JLoggerToLog4j(org.apache.logging.log4j.Logger logger) {
super("logger", null);
this.base = logger;
}
public void log(LogRecord record) {
this.log(record.getLevel(), record.getMessage());
}
public void log(Level level, String msg) {
if (level == Level.FINE) {
this.base.debug(msg);
} else if (level == Level.WARNING) {
this.base.warn(msg);
} else if (level == Level.SEVERE) {
this.base.error(msg);
} else if (level == Level.INFO) {
this.base.info(msg);
} else {
this.base.trace(msg);
}
}
public void log(Level level, String msg, Object param1) {
if (level == Level.FINE) {
this.base.debug(msg, param1);
} else if (level == Level.WARNING) {
this.base.warn(msg, param1);
} else if (level == Level.SEVERE) {
this.base.error(msg, param1);
} else if (level == Level.INFO) {
this.base.info(msg, param1);
} else {
this.base.trace(msg, param1);
}
}
public void log(Level level, String msg, Object[] params) {
log(level, MessageFormat.format(msg, params));
}
public void log(Level level, String msg, Throwable params) {
if (level == Level.FINE) {
this.base.debug(msg, params);
} else if (level == Level.WARNING) {
this.base.warn(msg, params);
} else if (level == Level.SEVERE) {
this.base.error(msg, params);
} else if (level == Level.INFO) {
this.base.info(msg, params);
} else {
this.base.trace(msg, params);
}
}
}

View File

@ -0,0 +1,60 @@
package com.viaversion.fabric.mc116.util;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.Arrays;
import java.util.stream.Stream;
public class ProtocolUtils {
public static boolean isSupported(int server, int client) {
return server == client || Via.getManager().getProtocolManager().getProtocolPath(client, server) != null;
}
public static String getProtocolName(int id) {
if (!ProtocolVersion.isRegistered(id)) return Integer.toString(id);
return ProtocolVersion.getProtocol(id).getName();
}
public static boolean isStartOfProtocolText(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
try {
Integer.parseInt(s + '0');
return true;
} catch (NumberFormatException e2) {
return ProtocolVersion.getProtocols().stream()
.map(ProtocolVersion::getName)
.flatMap(str -> Stream.concat(
Arrays.stream(str.split("-")),
Arrays.stream(new String[]{str})
))
.anyMatch(ver -> ver.startsWith(s));
}
}
}
public static Integer parseProtocolId(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ignored) {
ProtocolVersion closest = ProtocolVersion.getClosest(s);
if (closest == null) return null;
return closest.getVersion();
}
}
public static String[] getProtocolSuggestions(String text) {
return ProtocolVersion.getProtocols().stream()
.map(ProtocolVersion::getName)
.flatMap(str -> Stream.concat(
Arrays.stream(str.split("-")),
Arrays.stream(new String[]{str})
))
.distinct()
.filter(ver -> ver.startsWith(text))
.toArray(String[]::new);
}
}

View File

@ -0,0 +1,57 @@
{
"schemaVersion": 1,
"id": "viafabric-mc116",
"name": "ViaFabric",
"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.mc116.ViaFabric"
],
"modmenu": [
"com.viaversion.fabric.mc116.gui.ModMenuConfig"
]
},
"depends": {
"fabricloader": ">=0.4.0",
"fabric-resource-loader-v0": "*",
"minecraft": "1.16.x",
"viaversion": ">3.0.1"
},
"conflicts": {
"fabric-registry-sync-v0": "*"
},
"recommends": {
"fabric-command-api-v1": "*"
},
"icon": "assets/viafabric/textures/logo.png",
"mixins": [
"mixins.viafabric.address.json",
"mixins.viafabric.gui.json",
"mixins.viafabric.debug.json",
"mixins.viafabric.pipeline.json"
]
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc116.mixin.address.client.MixinConnectScreenThread",
"com.viaversion.fabric.mc116.mixin.address.client.MixinServerAddress",
"com.viaversion.fabric.mc116.mixin.address.client.MixinServerPinger"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc116.mixin.debug.client.MixinClientConnectionAccessor",
"com.viaversion.fabric.mc116.mixin.debug.client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"com.viaversion.fabric.mc116.mixin.gui.client.MixinMultiplayerScreen"
],
"injectors": {
"defaultRequire": 0
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"mixins": [
"com.viaversion.fabric.mc116.mixin.pipeline.MixinClientConnection",
"com.viaversion.fabric.mc116.mixin.pipeline.MixinServerNetworkIoChInit"
],
"client": [
"com.viaversion.fabric.mc116.mixin.pipeline.client.MixinClientConnectionChInit"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"id": "viafabric-impl",
"id": "viafabric-mc117",
"name": "ViaFabric",
"version": "@version@",
"description": "@description@",
@ -8,8 +8,7 @@
"contact": {
"homepage": "https://viaversion.com/fabric",
"issues": "https://github.com/ViaVersion/ViaFabric/issues",
"sources": "https://github.com/ViaVersion/ViaFabric",
"discord": "https://discord.gg/viaversion"
"sources": "https://github.com/ViaVersion/ViaFabric"
},
"environment": "*",
"authors": [

View File

@ -1,13 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc117.mixin.address",
"mixins": [
],
"client": [
"client.MixinConnectScreenThread",
"client.MixinServerAddress",
"client.MixinServerPinger"
"com.viaversion.fabric.mc117.mixin.address.client.MixinConnectScreenThread",
"com.viaversion.fabric.mc117.mixin.address.client.MixinServerAddress",
"com.viaversion.fabric.mc117.mixin.address.client.MixinServerPinger"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,12 +1,11 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc117.mixin.debug",
"mixins": [
],
"client": [
"client.MixinClientConnectionAccessor",
"client.MixinDebugHud"
"com.viaversion.fabric.mc117.mixin.debug.client.MixinClientConnectionAccessor",
"com.viaversion.fabric.mc117.mixin.debug.client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0

View File

@ -1,11 +1,10 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc117.mixin.gui",
"mixins": [
],
"client": [
"client.MixinMultiplayerScreen"
"com.viaversion.fabric.mc117.mixin.gui.client.MixinMultiplayerScreen"
],
"injectors": {
"defaultRequire": 0

View File

@ -1,13 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc117.mixin.pipeline",
"mixins": [
"MixinClientConnection",
"MixinServerNetworkIoChInit"
"com.viaversion.fabric.mc117.mixin.pipeline.MixinClientConnection",
"com.viaversion.fabric.mc117.mixin.pipeline.MixinServerNetworkIoChInit"
],
"client": [
"client.MixinClientConnectionChInit"
"com.viaversion.fabric.mc117.mixin.pipeline.client.MixinClientConnectionChInit"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,3 +1,4 @@
import java.util.function.Function as JFun
version = rootProject.version
dependencies {
@ -8,3 +9,9 @@ dependencies {
modImplementation("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:1.1.0+1.8.9")
modImplementation("io.github.boogiemonster1o1:modmenu:0.1.0+1.8.9")
}
minecraft {
intermediaryUrl = JFun {
"https://maven.legacyfabric.net/net/fabricmc/intermediary/$it/intermediary-$it-v2.jar"
}
}

View File

@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"id": "viafabric-impl",
"id": "viafabric-mc18",
"name": "ViaFabric",
"version": "@version@",
"description": "@description@",

View File

@ -1,13 +1,12 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc18.mixin.address",
"mixins": [
],
"client": [
"client.MixinConnectScreenThread",
"client.MixinServerAddress",
"client.MixinServerPinger"
"com.viaversion.fabric.mc18.mixin.address.client.MixinConnectScreenThread",
"com.viaversion.fabric.mc18.mixin.address.client.MixinServerAddress",
"com.viaversion.fabric.mc18.mixin.address.client.MixinServerPinger"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,12 +1,11 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc18.mixin.debug",
"mixins": [
],
"client": [
"client.MixinClientConnectionAccessor",
"client.MixinDebugHud"
"com.viaversion.fabric.mc18.mixin.debug.client.MixinClientConnectionAccessor",
"com.viaversion.fabric.mc18.mixin.debug.client.MixinDebugHud"
],
"injectors": {
"defaultRequire": 0

View File

@ -1,11 +1,10 @@
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.viaversion.fabric.mc18.mixin.gui",
"mixins": [
],
"client": [
"client.MixinMultiplayerScreen"
"com.viaversion.fabric.mc18.mixin.gui.client.MixinMultiplayerScreen"
],
"injectors": {
"defaultRequire": 0

Some files were not shown because too many files have changed in this diff Show More