3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-12-23 23:00:35 +01:00

Initial commit. Very broken and only does Server List Ping!

Dieser Commit ist enthalten in:
Andrew Steinborn 2018-07-24 14:08:55 -04:00
Commit 666d07e2a8
29 geänderte Dateien mit 1264 neuen und 0 gelöschten Zeilen

119
.gitignore vendored Normale Datei
Datei anzeigen

@ -0,0 +1,119 @@
# Created by https://www.gitignore.io/api/java,gradle,intellij
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Gradle ###
.gradle
/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
# End of https://www.gitignore.io/api/java,gradle,intellij

7
.idea/misc.xml generiert Normale Datei
Datei anzeigen

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generiert Normale Datei
Datei anzeigen

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

21
build.gradle Normale Datei
Datei anzeigen

@ -0,0 +1,21 @@
plugins {
id 'java'
}
group 'io.minimum.minecraft'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
compile 'io.netty:netty-all:4.1.27.Final'
compile 'com.google.guava:guava:25.1-jre'
compile 'com.google.code.gson:gson:2.8.5'
compile 'com.github.KyoriPowered:text:v1.12-1.5.0'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normale Datei

Binäre Datei nicht angezeigt.

6
gradle/wrapper/gradle-wrapper.properties vendored Normale Datei
Datei anzeigen

@ -0,0 +1,6 @@
#Mon Jul 23 20:52:34 EDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip

172
gradlew vendored Ausführbare Datei
Datei anzeigen

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normale Datei
Datei anzeigen

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
settings.gradle Normale Datei
Datei anzeigen

@ -0,0 +1,2 @@
rootProject.name = 'velocity'

Datei anzeigen

@ -0,0 +1,32 @@
package io.minimum.minecraft.velocity;
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
import io.minimum.minecraft.velocity.protocol.netty.*;
import io.minimum.minecraft.velocity.proxy.MinecraftClientSessionHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Velocity {
public static void main(String... args) throws InterruptedException {
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(new NioEventLoopGroup())
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast("legacy-ping-decode", new LegacyPingDecoder());
ch.pipeline().addLast("frame-decoder", new MinecraftVarintFrameDecoder());
ch.pipeline().addLast("legacy-ping-encode", new LegacyPingEncoder());
ch.pipeline().addLast("frame-encoder", new MinecraftVarintLengthEncoder());
ch.pipeline().addLast("minecraft-decoder", new MinecraftDecoder(ProtocolConstants.Direction.TO_SERVER));
ch.pipeline().addLast("minecraft-encoder", new MinecraftEncoder(ProtocolConstants.Direction.TO_CLIENT));
ch.pipeline().addLast("handler", new MinecraftClientSessionHandler());
}
})
.bind(26671)
.await();
}
}

Datei anzeigen

@ -0,0 +1,95 @@
package io.minimum.minecraft.velocity.data;
import net.kyori.text.Component;
public class ServerPing {
private final Version version;
private final Players players;
private final Component description;
private final String favicon;
public ServerPing(Version version, Players players, Component description, String favicon) {
this.version = version;
this.players = players;
this.description = description;
this.favicon = favicon;
}
public Version getVersion() {
return version;
}
public Players getPlayers() {
return players;
}
public Component getDescription() {
return description;
}
public String getFavicon() {
return favicon;
}
@Override
public String toString() {
return "ServerPing{" +
"version=" + version +
", players=" + players +
", description=" + description +
", favicon='" + favicon + '\'' +
'}';
}
public static class Version {
private final int protocol;
private final String version;
public Version(int protocol, String version) {
this.protocol = protocol;
this.version = version;
}
public int getProtocol() {
return protocol;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
return "Version{" +
"protocol=" + protocol +
", version='" + version + '\'' +
'}';
}
}
public static class Players {
private final int online;
private final int max;
public Players(int online, int max) {
this.online = online;
this.max = max;
}
public int getOnline() {
return online;
}
public int getMax() {
return max;
}
@Override
public String toString() {
return "Players{" +
"online=" + online +
", max=" + max +
'}';
}
}
}

Datei anzeigen

@ -0,0 +1,9 @@
package io.minimum.minecraft.velocity.protocol;
import io.netty.buffer.ByteBuf;
public interface MinecraftPacket {
void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion);
void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion);
}

Datei anzeigen

@ -0,0 +1,30 @@
package io.minimum.minecraft.velocity.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
public class PacketWrapper {
private final MinecraftPacket packet;
private final ByteBuf buffer;
public PacketWrapper(MinecraftPacket packet, ByteBuf buffer) {
this.packet = packet;
this.buffer = buffer;
}
public MinecraftPacket getPacket() {
return packet;
}
public ByteBuf getBuffer() {
return buffer;
}
@Override
public String toString() {
return "PacketWrapper{" +
"packet=" + packet +
", buffer=" + ByteBufUtil.hexDump(buffer) +
'}';
}
}

Datei anzeigen

@ -0,0 +1,10 @@
package io.minimum.minecraft.velocity.protocol;
public enum ProtocolConstants { ;
public static final int MINECRAFT_1_12 = 340;
public enum Direction {
TO_SERVER,
TO_CLIENT
}
}

Datei anzeigen

@ -0,0 +1,58 @@
package io.minimum.minecraft.velocity.protocol;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import java.nio.charset.StandardCharsets;
public enum ProtocolUtils { ;
private static final int DEFAULT_MAX_STRING_SIZE = 1024 * 1024; // 1MB
public static int readVarInt(ByteBuf buf) {
int numRead = 0;
int result = 0;
byte read;
do {
read = buf.readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 5) {
throw new RuntimeException("VarInt is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
public static void writeVarInt(ByteBuf buf, int value) {
do {
byte temp = (byte)(value & 0b01111111);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
buf.writeByte(temp);
} while (value != 0);
}
public static String readString(ByteBuf buf) {
return readString(buf, DEFAULT_MAX_STRING_SIZE);
}
public static String readString(ByteBuf buf, int cap) {
int length = readVarInt(buf);
Preconditions.checkArgument(length < cap, "Bad string size (got %s, maximum is %s)", length, cap);
byte[] str = new byte[length];
buf.readBytes(str);
return new String(str, StandardCharsets.UTF_8);
}
public static void writeString(ByteBuf buf, String str) {
byte[] asUtf8 = str.getBytes(StandardCharsets.UTF_8);
writeVarInt(buf, asUtf8.length);
buf.writeBytes(asUtf8);
}
}

Datei anzeigen

@ -0,0 +1,63 @@
package io.minimum.minecraft.velocity.protocol;
import io.minimum.minecraft.velocity.protocol.packets.Handshake;
import io.minimum.minecraft.velocity.protocol.packets.Ping;
import io.minimum.minecraft.velocity.protocol.packets.StatusRequest;
import io.minimum.minecraft.velocity.protocol.packets.StatusResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public enum StateRegistry {
HANDSHAKE {
{
TO_SERVER.register(0x00, Handshake.class, Handshake::new);
}
},
STATUS {
{
TO_SERVER.register(0x00, StatusRequest.class, StatusRequest::new);
TO_SERVER.register(0x01, Ping.class, Ping::new);
TO_CLIENT.register(0x00, StatusResponse.class, StatusResponse::new);
TO_CLIENT.register(0x01, Ping.class, Ping::new);
}
};
public final ProtocolMappings TO_CLIENT = new ProtocolMappings(ProtocolConstants.Direction.TO_CLIENT, this);
public final ProtocolMappings TO_SERVER = new ProtocolMappings(ProtocolConstants.Direction.TO_SERVER, this);
public static class ProtocolMappings {
private final ProtocolConstants.Direction direction;
private final StateRegistry state;
private final Map<Integer, Supplier<? extends MinecraftPacket>> idsToSuppliers = new HashMap<>();
private final Map<Class<? extends MinecraftPacket>, Integer> packetClassesToIds = new HashMap<>();
public ProtocolMappings(ProtocolConstants.Direction direction, StateRegistry state) {
this.direction = direction;
this.state = state;
}
public void register(int id, Class<? extends MinecraftPacket> clazz, Supplier<? extends MinecraftPacket> packetSupplier) {
idsToSuppliers.put(id, packetSupplier);
packetClassesToIds.put(clazz, id);
}
public MinecraftPacket createPacket(int id) {
Supplier<? extends MinecraftPacket> supplier = idsToSuppliers.get(id);
if (supplier == null) {
return null;
}
return supplier.get();
}
public int getId(MinecraftPacket packet) {
Integer id = packetClassesToIds.get(packet.getClass());
if (id == null) {
throw new IllegalArgumentException("Supplied packet " + packet.getClass().getName() + " doesn't have a mapping. Direction " + direction + " State " + state);
}
return id;
}
}
}

Datei anzeigen

@ -0,0 +1,26 @@
package io.minimum.minecraft.velocity.protocol.netty;
import io.minimum.minecraft.velocity.protocol.packets.LegacyPing;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class LegacyPingDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 2) {
return;
}
short first = in.getUnsignedByte(in.readerIndex());
short second = in.getUnsignedByte(in.readerIndex() + 1);
if (first == 0xfe && second == 0x01) {
in.skipBytes(in.readableBytes());
out.add(new LegacyPing());
}
ctx.pipeline().remove(this);
}
}

Datei anzeigen

@ -0,0 +1,36 @@
package io.minimum.minecraft.velocity.protocol.netty;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import io.minimum.minecraft.velocity.protocol.packets.LegacyPingResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class LegacyPingEncoder extends MessageToByteEncoder<LegacyPingResponse> {
@Override
protected void encode(ChannelHandlerContext ctx, LegacyPingResponse msg, ByteBuf out) throws Exception {
out.writeByte(0xff);
String serializedResponse = serialize(msg);
byte[] serializedBytes = serializedResponse.getBytes(StandardCharsets.UTF_16BE);
out.writeShort(serializedBytes.length);
out.writeBytes(serializedBytes);
System.out.println(ByteBufUtil.prettyHexDump(out));
}
private String serialize(LegacyPingResponse response) {
List<String> parts = ImmutableList.of(
"§1",
Integer.toString(response.getProtocolVersion()),
response.getServerVersion(),
response.getMotd(),
Integer.toString(response.getPlayersOnline()),
Integer.toString(response.getPlayersMax())
);
return Joiner.on('\0').join(parts);
}
}

Datei anzeigen

@ -0,0 +1,64 @@
package io.minimum.minecraft.velocity.protocol.netty;
import com.google.common.base.Preconditions;
import io.minimum.minecraft.velocity.protocol.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
private StateRegistry state;
private final ProtocolConstants.Direction direction;
private int protocolVersion;
public MinecraftDecoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction");
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
if (msg.isReadable()) {
return;
}
ByteBuf slice = msg.slice().retain();
int packetId = ProtocolUtils.readVarInt(msg);
StateRegistry.ProtocolMappings mappings = direction == ProtocolConstants.Direction.TO_CLIENT ? state.TO_CLIENT : state.TO_SERVER;
MinecraftPacket packet = mappings.createPacket(packetId);
System.out.println("Decode!");
System.out.println("packet ID: " + packetId);
System.out.println("packet hexdump: " + ByteBufUtil.hexDump(slice));
if (packet == null) {
msg.skipBytes(msg.readableBytes());
out.add(new PacketWrapper(null, slice));
} else {
packet.decode(msg, direction, protocolVersion);
out.add(new PacketWrapper(packet, slice));
}
}
public int getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
public StateRegistry getState() {
return state;
}
public void setState(StateRegistry state) {
this.state = state;
}
public ProtocolConstants.Direction getDirection() {
return direction;
}
}

Datei anzeigen

@ -0,0 +1,46 @@
package io.minimum.minecraft.velocity.protocol.netty;
import com.google.common.base.Preconditions;
import io.minimum.minecraft.velocity.protocol.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
private StateRegistry state;
private final ProtocolConstants.Direction direction;
private int protocolVersion;
public MinecraftEncoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction");
}
@Override
protected void encode(ChannelHandlerContext ctx, MinecraftPacket msg, ByteBuf out) throws Exception {
StateRegistry.ProtocolMappings mappings = direction == ProtocolConstants.Direction.TO_CLIENT ? state.TO_CLIENT : state.TO_SERVER;
int packetId = mappings.getId(msg);
ProtocolUtils.writeVarInt(out, packetId);
msg.encode(out, direction, protocolVersion);
}
public int getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
public StateRegistry getState() {
return state;
}
public void setState(StateRegistry state) {
this.state = state;
}
public ProtocolConstants.Direction getDirection() {
return direction;
}
}

Datei anzeigen

@ -0,0 +1,28 @@
package io.minimum.minecraft.velocity.protocol.netty;
import io.minimum.minecraft.velocity.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 1) {
return;
}
in.markReaderIndex();
int packetLength = ProtocolUtils.readVarInt(in);
if (in.readableBytes() < packetLength) {
in.resetReaderIndex();
return;
}
System.out.println("Got a varint-prefixed packet length " + packetLength);
out.add(in.slice(in.readerIndex(), packetLength).retain());
in.skipBytes(packetLength);
}
}

Datei anzeigen

@ -0,0 +1,14 @@
package io.minimum.minecraft.velocity.protocol.netty;
import io.minimum.minecraft.velocity.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MinecraftVarintLengthEncoder extends MessageToByteEncoder<ByteBuf> {
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
ProtocolUtils.writeVarInt(out, msg.readableBytes());
out.writeBytes(msg);
}
}

Datei anzeigen

@ -0,0 +1,71 @@
package io.minimum.minecraft.velocity.protocol.packets;
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
import io.minimum.minecraft.velocity.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class Handshake implements MinecraftPacket {
private int protocolVersion;
private String serverAddress;
private int port;
private int nextStatus;
public int getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
public String getServerAddress() {
return serverAddress;
}
public void setServerAddress(String serverAddress) {
this.serverAddress = serverAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getNextStatus() {
return nextStatus;
}
public void setNextStatus(int nextStatus) {
this.nextStatus = nextStatus;
}
@Override
public String toString() {
return "Handshake{" +
"protocolVersion=" + protocolVersion +
", serverAddress='" + serverAddress + '\'' +
", port=" + port +
", nextStatus=" + nextStatus +
'}';
}
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
protocolVersion = ProtocolUtils.readVarInt(buf);
serverAddress = ProtocolUtils.readString(buf, 255);
port = buf.readUnsignedShort();
nextStatus = ProtocolUtils.readVarInt(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeVarInt(buf, protocolVersion);
ProtocolUtils.writeString(buf, serverAddress);
buf.writeShort(port);
ProtocolUtils.writeVarInt(buf, nextStatus);
}
}

Datei anzeigen

@ -0,0 +1,4 @@
package io.minimum.minecraft.velocity.protocol.packets;
public class LegacyPing {
}

Datei anzeigen

@ -0,0 +1,82 @@
package io.minimum.minecraft.velocity.protocol.packets;
import io.minimum.minecraft.velocity.data.ServerPing;
import net.kyori.text.serializer.ComponentSerializers;
public class LegacyPingResponse {
private int protocolVersion;
private String serverVersion;
private String motd;
private int playersOnline;
private int playersMax;
public LegacyPingResponse() {
}
public LegacyPingResponse(int protocolVersion, String serverVersion, String motd, int playersOnline, int playersMax) {
this.protocolVersion = protocolVersion;
this.serverVersion = serverVersion;
this.motd = motd;
this.playersOnline = playersOnline;
this.playersMax = playersMax;
}
public int getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
public String getServerVersion() {
return serverVersion;
}
public void setServerVersion(String serverVersion) {
this.serverVersion = serverVersion;
}
public String getMotd() {
return motd;
}
public void setMotd(String motd) {
this.motd = motd;
}
public int getPlayersOnline() {
return playersOnline;
}
public void setPlayersOnline(int playersOnline) {
this.playersOnline = playersOnline;
}
public int getPlayersMax() {
return playersMax;
}
public void setPlayersMax(int playersMax) {
this.playersMax = playersMax;
}
@Override
public String toString() {
return "LegacyPingResponse{" +
"protocolVersion=" + protocolVersion +
", serverVersion='" + serverVersion + '\'' +
", motd='" + motd + '\'' +
", playersOnline=" + playersOnline +
", playersMax=" + playersMax +
'}';
}
public static LegacyPingResponse from(ServerPing ping) {
return new LegacyPingResponse(ping.getVersion().getProtocol(),
ping.getVersion().getVersion(),
ComponentSerializers.LEGACY.serialize(ping.getDescription()),
ping.getPlayers().getOnline(),
ping.getPlayers().getMax());
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package io.minimum.minecraft.velocity.protocol.packets;
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
import io.netty.buffer.ByteBuf;
public class Ping implements MinecraftPacket {
private long randomId;
public long getRandomId() {
return randomId;
}
public void setRandomId(long randomId) {
this.randomId = randomId;
}
@Override
public String toString() {
return "Ping{" +
"randomId=" + randomId +
'}';
}
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
randomId = buf.readLong();
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
buf.writeLong(randomId);
}
}

Datei anzeigen

@ -0,0 +1,17 @@
package io.minimum.minecraft.velocity.protocol.packets;
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
import io.netty.buffer.ByteBuf;
public class StatusRequest implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
}
}

Datei anzeigen

@ -0,0 +1,35 @@
package io.minimum.minecraft.velocity.protocol.packets;
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
import io.minimum.minecraft.velocity.protocol.ProtocolConstants;
import io.minimum.minecraft.velocity.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class StatusResponse implements MinecraftPacket {
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "StatusResponse{" +
"status='" + status + '\'' +
'}';
}
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
status = ProtocolUtils.readString(buf, Short.MAX_VALUE);
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeString(buf, status);
}
}

Datei anzeigen

@ -0,0 +1,93 @@
package io.minimum.minecraft.velocity.proxy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.minimum.minecraft.velocity.data.ServerPing;
import io.minimum.minecraft.velocity.protocol.MinecraftPacket;
import io.minimum.minecraft.velocity.protocol.PacketWrapper;
import io.minimum.minecraft.velocity.protocol.StateRegistry;
import io.minimum.minecraft.velocity.protocol.netty.MinecraftDecoder;
import io.minimum.minecraft.velocity.protocol.netty.MinecraftEncoder;
import io.minimum.minecraft.velocity.protocol.packets.*;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.serializer.GsonComponentSerializer;
public class MinecraftClientSessionHandler extends ChannelInboundHandlerAdapter {
private static final Gson GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
.create();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof PacketWrapper) {
try {
handle(ctx, (PacketWrapper) msg);
} finally {
((PacketWrapper) msg).getBuffer().release();
}
}
if (msg instanceof LegacyPing) {
System.out.println("Got LEGACY status request!");
ServerPing ping = new ServerPing(
new ServerPing.Version(340, "1.12"),
new ServerPing.Players(0, 0),
TextComponent.of("this is a test"),
null
);
LegacyPingResponse response = LegacyPingResponse.from(ping);
ctx.writeAndFlush(response);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
private void handle(ChannelHandlerContext ctx, PacketWrapper msg) {
MinecraftPacket packet = msg.getPacket();
if (packet == null) {
System.out.println("no packet!");
return;
}
if (packet instanceof Handshake) {
System.out.println("Handshake: " + packet);
switch (((Handshake) packet).getNextStatus()) {
case 1:
// status
ctx.pipeline().get(MinecraftDecoder.class).setState(StateRegistry.STATUS);
ctx.pipeline().get(MinecraftEncoder.class).setState(StateRegistry.STATUS);
break;
case 2:
// login
throw new UnsupportedOperationException("Login not supported yet");
}
}
if (packet instanceof StatusRequest) {
System.out.println("Got status request!");
ServerPing ping = new ServerPing(
new ServerPing.Version(340, "1.12.2"),
new ServerPing.Players(0, 0),
TextComponent.of("test"),
null
);
StatusResponse response = new StatusResponse();
response.setStatus(GSON.toJson(ping));
ctx.writeAndFlush(response, ctx.voidPromise());
}
if (packet instanceof Ping) {
System.out.println("Ping: " + packet);
ctx.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
}
}
}