mirror of
https://github.com/ViaVersion/ViaFabric.git
synced 2024-11-25 12:15:14 +01:00
subprojects part 2
This commit is contained in:
parent
e49f1539ed
commit
c09d6f0a74
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -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']
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -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
|
||||
|
30
build.gradle
30
build.gradle
@ -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 {
|
||||
@ -134,8 +133,8 @@ allprojects {
|
||||
processResources {
|
||||
filesMatching("fabric.mod.json") {
|
||||
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
|
||||
version: version,
|
||||
description: description
|
||||
version : version,
|
||||
description: description
|
||||
])
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
@ -12,4 +12,6 @@ rootProject.name = "viafabric"
|
||||
|
||||
include "viafabric-mc18"
|
||||
include "viafabric-mc114"
|
||||
include "viafabric-mc115"
|
||||
include "viafabric-mc116"
|
||||
include "viafabric-mc117"
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "viafabric-impl",
|
||||
"id": "viafabric-mc114",
|
||||
"name": "ViaFabric",
|
||||
"version": "@version@",
|
||||
"description": "@description@",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
@ -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")));
|
||||
}
|
||||
}
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -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 "?";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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> {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
61
viafabric-mc115/src/main/resources/fabric.mod.json
Normal file
61
viafabric-mc115/src/main/resources/fabric.mod.json
Normal 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"
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
12
viafabric-mc115/src/main/resources/mixins.viafabric.gui.json
Normal file
12
viafabric-mc115/src/main/resources/mixins.viafabric.gui.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
"com.viaversion.fabric.mc115.mixin.gui.client.MixinMultiplayerScreen"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 0
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")));
|
||||
}
|
||||
}
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -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 "?";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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> {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
57
viafabric-mc116/src/main/resources/fabric.mod.json
Normal file
57
viafabric-mc116/src/main/resources/fabric.mod.json
Normal 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"
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
12
viafabric-mc116/src/main/resources/mixins.viafabric.gui.json
Normal file
12
viafabric-mc116/src/main/resources/mixins.viafabric.gui.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
"com.viaversion.fabric.mc116.mixin.gui.client.MixinMultiplayerScreen"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 0
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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": [
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,4 @@
|
||||
import java.util.function.Function as JFun
|
||||
version = rootProject.version
|
||||
|
||||
dependencies {
|
||||
@ -7,4 +8,10 @@ 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"
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "viafabric-impl",
|
||||
"id": "viafabric-mc18",
|
||||
"name": "ViaFabric",
|
||||
"version": "@version@",
|
||||
"description": "@description@",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user