Initial commit

This commit is contained in:
TheMode 2019-08-03 15:25:24 +02:00
commit 9380770492
38 changed files with 1222 additions and 0 deletions

18
build.gradle Normal file
View File

@ -0,0 +1,18 @@
plugins {
id 'java'
}
group 'fr.themode.minestom'
version '1.0'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
implementation 'com.github.Adamaq01:ozao-net:2.3.1'
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Thu Aug 01 13:49:10 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip

172
gradlew vendored Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

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

View File

@ -0,0 +1,61 @@
package fr.themode.minestom;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.adamaq01.ozao.net.server.Server;
import fr.adamaq01.ozao.net.server.ServerHandler;
import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.PacketProcessor;
import fr.themode.minestom.net.protocol.MinecraftProtocol;
import java.lang.reflect.InvocationTargetException;
public class Main {
private static ConnectionManager connectionManager;
private static PacketProcessor packetProcessor;
public static void main(String[] args) {
connectionManager = new ConnectionManager();
packetProcessor = new PacketProcessor(connectionManager);
Server server = new TCPServer(new MinecraftProtocol()).addHandler(new ServerHandler() {
@Override
public void onConnect(Server server, Connection connection) {
System.out.println("A connection");
}
@Override
public void onDisconnect(Server server, Connection connection) {
System.out.println("A DISCONNECTION");
}
@Override
public void onPacketReceive(Server server, Connection connection, Packet packet) {
try {
packetProcessor.process(connection, packet);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
@Override
public void onException(Server server, Connection connection, Throwable cause) {
cause.printStackTrace();
}
});
server.bind(25565);
System.out.println("Server started");
}
}

View File

@ -0,0 +1,25 @@
package fr.themode.minestom.entity;
public enum GameMode {
SURVIVAL((byte) 0), CREATIVE((byte) 1), ADVENTURE((byte) 2), SPECTATOR((byte) 3);
private byte id;
private boolean hardcore;
GameMode(byte id) {
this.id = id;
}
public void setHardcore(boolean hardcore) {
this.hardcore = hardcore;
}
public byte getId() {
return id;
}
public boolean isHardcore() {
return hardcore;
}
}

View File

@ -0,0 +1,12 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.net.player.PlayerConnection;
public class Player {
private PlayerConnection playerConnection;
public PlayerConnection getPlayerConnection() {
return playerConnection;
}
}

View File

@ -0,0 +1,24 @@
package fr.themode.minestom.net;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.player.PlayerConnection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ConnectionManager {
private Set<PlayerConnection> connections = new HashSet<>();
private Map<PlayerConnection, Player> connectionPlayerMap = new HashMap<>();
public Player getPlayer(PlayerConnection connection) {
return connectionPlayerMap.get(connection);
}
// Is only used at LoginStartPacket#process
public void createPlayer(PlayerConnection connection) {
this.connectionPlayerMap.put(connection, new Player());
}
}

View File

@ -0,0 +1,7 @@
package fr.themode.minestom.net;
public enum ConnectionState {
UNKNOWN, STATUS, LOGIN, PLAY;
}

View File

@ -0,0 +1,80 @@
package fr.themode.minestom.net;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.packet.client.handler.ClientLoginPacketsHandler;
import fr.themode.minestom.net.packet.client.handler.ClientPlayPacketsHandler;
import fr.themode.minestom.net.packet.client.handler.ClientStatusPacketsHandler;
import fr.themode.minestom.net.packet.client.handshake.HandshakePacket;
import fr.themode.minestom.net.player.PlayerConnection;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER;
public class PacketProcessor {
private Map<Connection, PlayerConnection> connectionPlayerConnectionMap = new HashMap<>();
private ConnectionManager connectionManager;
// Protocols
private ClientStatusPacketsHandler statusPacketsHandler;
private ClientLoginPacketsHandler loginPacketsHandler;
private ClientPlayPacketsHandler playPacketsHandler;
public PacketProcessor(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
this.statusPacketsHandler = new ClientStatusPacketsHandler();
this.loginPacketsHandler = new ClientLoginPacketsHandler();
this.playPacketsHandler = new ClientPlayPacketsHandler();
}
public void process(Connection connection, Packet packet) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
int id = packet.get(PACKET_ID_IDENTIFIER);
System.out.println("RECEIVED ID: " + id);
Buffer buffer = packet.getPayload();
connectionPlayerConnectionMap.get(connection);
PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent(connection, c -> new PlayerConnection(c));
ConnectionState connectionState = playerConnection.getConnectionState();
if (connectionState == ConnectionState.UNKNOWN) {
// Should be handshake packet
if (id == 0) {
HandshakePacket handshakePacket = new HandshakePacket();
handshakePacket.read(buffer);
handshakePacket.process(playerConnection, connectionManager);
}
return;
}
switch (connectionState) {
case PLAY:
Player player = connectionManager.getPlayer(playerConnection);
ClientPlayPacket playPacket = (ClientPlayPacket) playPacketsHandler.getPacketClass(id).getDeclaredConstructor().newInstance();
playPacket.read(buffer);
playPacket.process(player);
break;
case LOGIN:
ClientPreplayPacket loginPacket = (ClientPreplayPacket) loginPacketsHandler.getPacketClass(id).getDeclaredConstructor().newInstance();
loginPacket.read(buffer);
loginPacket.process(playerConnection, connectionManager);
break;
case STATUS:
ClientPreplayPacket statusPacket = (ClientPreplayPacket) statusPacketsHandler.getPacketClass(id).getDeclaredConstructor().newInstance();
statusPacket.read(buffer);
statusPacket.process(playerConnection, connectionManager);
break;
case UNKNOWN:
// Ignore packet (unexpected)
break;
}
}
}

View File

@ -0,0 +1,9 @@
package fr.themode.minestom.net.packet.client;
import fr.adamaq01.ozao.net.Buffer;
public interface ClientPacket {
void read(Buffer buffer);
}

View File

@ -0,0 +1,10 @@
package fr.themode.minestom.net.packet.client;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.ClientPacket;
public interface ClientPlayPacket extends ClientPacket {
void process(Player player);
}

View File

@ -0,0 +1,9 @@
package fr.themode.minestom.net.packet.client;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.player.PlayerConnection;
public interface ClientPreplayPacket extends ClientPacket {
void process(PlayerConnection connection, ConnectionManager connectionManager);
}

View File

@ -0,0 +1,11 @@
package fr.themode.minestom.net.packet.client.handler;
import fr.themode.minestom.net.packet.client.login.LoginStartPacket;
public class ClientLoginPacketsHandler extends ClientPacketsHandler {
public ClientLoginPacketsHandler() {
register(0, LoginStartPacket.class);
}
}

View File

@ -0,0 +1,20 @@
package fr.themode.minestom.net.packet.client.handler;
import fr.themode.minestom.net.packet.client.ClientPacket;
import java.util.HashMap;
import java.util.Map;
public class ClientPacketsHandler {
private Map<Integer, Class<? extends ClientPacket>> idPacketMap = new HashMap<>();
public void register(int id, Class<? extends ClientPacket> packet) {
this.idPacketMap.put(id, packet);
}
public Class<? extends ClientPacket> getPacketClass(int id) {
return idPacketMap.get(id);
}
}

View File

@ -0,0 +1,13 @@
package fr.themode.minestom.net.packet.client.handler;
import fr.themode.minestom.net.packet.client.play.ClientPluginMessagePacket;
import fr.themode.minestom.net.packet.client.play.ClientSettingsPacket;
public class ClientPlayPacketsHandler extends ClientPacketsHandler {
public ClientPlayPacketsHandler() {
register(0x05, ClientSettingsPacket.class);
register(0x0B, ClientPluginMessagePacket.class);
}
}

View File

@ -0,0 +1,13 @@
package fr.themode.minestom.net.packet.client.handler;
import fr.themode.minestom.net.packet.client.status.PingPacket;
import fr.themode.minestom.net.packet.client.status.StatusRequestPacket;
public class ClientStatusPacketsHandler extends ClientPacketsHandler {
public ClientStatusPacketsHandler() {
register(0x00, StatusRequestPacket.class);
register(0x01, PingPacket.class);
}
}

View File

@ -0,0 +1,38 @@
package fr.themode.minestom.net.packet.client.handshake;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.player.PlayerConnection;
import static fr.themode.minestom.utils.Utils.readString;
import static fr.themode.minestom.utils.Utils.readVarInt;
public class HandshakePacket implements ClientPreplayPacket {
private int nextState;
@Override
public void read(Buffer buffer) {
int protocolVersion = readVarInt(buffer);
String serverAddress = readString(buffer);
short serverPort = buffer.getShort();
this.nextState = readVarInt(buffer);
}
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
switch (nextState) {
case 1:
connection.setConnectionState(ConnectionState.STATUS);
break;
case 2:
connection.setConnectionState(ConnectionState.LOGIN);
break;
default:
// Unexpected error
break;
}
}
}

View File

@ -0,0 +1,58 @@
package fr.themode.minestom.net.packet.client.login;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.packet.server.login.JoinGamePacket;
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.Utils;
import fr.themode.minestom.world.Dimension;
public class LoginStartPacket implements ClientPreplayPacket {
private String username;
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
// TODO send encryption request OR directly login success
LoginSuccessPacket successPacket = new LoginSuccessPacket(username);
connection.sendPacket(successPacket);
connection.setConnectionState(ConnectionState.PLAY);
connectionManager.createPlayer(connection);
// TODO complete login sequence with optionals packets
JoinGamePacket joinGamePacket = new JoinGamePacket();
joinGamePacket.entityId = 32;
joinGamePacket.gameMode = GameMode.SURVIVAL;
joinGamePacket.dimension = Dimension.OVERWORLD;
joinGamePacket.maxPlayers = 0;
joinGamePacket.levelType = "default";
joinGamePacket.reducedDebugInfo = false;
connection.sendPacket(joinGamePacket);
// TODO minecraft:brand plugin message
// TODO send server difficulty
// TODO player abilities
SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket();
spawnPositionPacket.x = 50;
spawnPositionPacket.y = 50;
spawnPositionPacket.z = 50;
// connection.sendPacket(spawnPositionPacket);
}
@Override
public void read(Buffer buffer) {
this.username = Utils.readString(buffer);
}
}

View File

@ -0,0 +1,23 @@
package fr.themode.minestom.net.packet.client.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.utils.Utils;
public class ClientPluginMessagePacket implements ClientPlayPacket {
private String identifier;
private byte[] data;
@Override
public void process(Player player) {
}
@Override
public void read(Buffer buffer) {
this.identifier = Utils.readString(buffer);
this.data = buffer.getAllBytes();
}
}

View File

@ -0,0 +1,31 @@
package fr.themode.minestom.net.packet.client.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.utils.Utils;
public class ClientSettingsPacket implements ClientPlayPacket {
private String locale;
private byte viewDistance;
// TODO chat mode
private boolean chatColors;
private byte displayedSkinParts;
// TODO main hand
@Override
public void process(Player player) {
}
@Override
public void read(Buffer buffer) {
this.locale = Utils.readString(buffer);
this.viewDistance = buffer.getByte();
Utils.readVarInt(buffer); // chat mode
this.chatColors = buffer.getBoolean();
this.displayedSkinParts = buffer.getByte();
Utils.readVarInt(buffer); // main hand
}
}

View File

@ -0,0 +1,18 @@
package fr.themode.minestom.net.packet.client.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
public class TestPacket implements ClientPlayPacket {
@Override
public void process(Player player) {
}
@Override
public void read(Buffer buffer) {
System.out.println("Hey c'est moi");
}
}

View File

@ -0,0 +1,24 @@
package fr.themode.minestom.net.packet.client.status;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.packet.server.status.PongPacket;
import fr.themode.minestom.net.player.PlayerConnection;
public class PingPacket implements ClientPreplayPacket {
private long number;
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
PongPacket pongPacket = new PongPacket(number);
connection.sendPacket(pongPacket);
connection.getConnection().close();
}
@Override
public void read(Buffer buffer) {
this.number = buffer.getLong();
}
}

View File

@ -0,0 +1,21 @@
package fr.themode.minestom.net.packet.client.status;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.packet.server.handshake.ResponsePacket;
import fr.themode.minestom.net.player.PlayerConnection;
public class StatusRequestPacket implements ClientPreplayPacket {
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
ResponsePacket responsePacket = new ResponsePacket();
connection.sendPacket(responsePacket);
}
@Override
public void read(Buffer buffer) {
// Empty
}
}

View File

@ -0,0 +1,11 @@
package fr.themode.minestom.net.packet.server;
import fr.adamaq01.ozao.net.Buffer;
public interface ServerPacket {
void write(Buffer buffer);
int getId();
}

View File

@ -0,0 +1,39 @@
package fr.themode.minestom.net.packet.server.handshake;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class ResponsePacket implements ServerPacket {
private static final String JSON_EXAMPLE = "{\n" +
" \"version\": {\n" +
" \"name\": \"1.14.4\",\n" +
" \"protocol\": 498\n" +
" },\n" +
" \"players\": {\n" +
" \"max\": 100,\n" +
" \"online\": 1,\n" +
" \"sample\": [\n" +
" {\n" +
" \"name\": \"TheMode\",\n" +
" \"id\": \"4566e69f-c907-48ee-8d71-d7ba5aa00d20\"\n" +
" }\n" +
" ]\n" +
" },\t\n" +
" \"description\": {\n" +
" \"text\": \"Wallah les cubes\"\n" +
" },\n" +
" \"favicon\": \"data:image/png;base64,<data>\"\n" +
"}";
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, JSON_EXAMPLE);
}
@Override
public int getId() {
return 0x00;
}
}

View File

@ -0,0 +1,37 @@
package fr.themode.minestom.net.packet.server.login;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
import fr.themode.minestom.world.Dimension;
public class JoinGamePacket implements ServerPacket {
public int entityId;
public GameMode gameMode = GameMode.SURVIVAL;
public Dimension dimension = Dimension.OVERWORLD;
public byte maxPlayers = 0; // Unused
public String levelType = "default";
public boolean reducedDebugInfo = false;
@Override
public void write(Buffer buffer) {
int gameModeId = gameMode.getId();
if (gameMode.isHardcore())
gameModeId |= 8;
buffer.putInt(entityId);
buffer.putByte((byte) gameModeId);
buffer.putInt(dimension.getId());
buffer.putByte(maxPlayers);
Utils.writeString(buffer, levelType);
Utils.writeVarInt(buffer, 8);
buffer.putBoolean(reducedDebugInfo);
}
@Override
public int getId() {
return 0x25;
}
}

View File

@ -0,0 +1,27 @@
package fr.themode.minestom.net.packet.server.login;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
import java.util.UUID;
public class LoginSuccessPacket implements ServerPacket {
public String username;
public LoginSuccessPacket(String username) {
this.username = username;
}
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, UUID.randomUUID().toString()); // TODO mojang auth
Utils.writeString(buffer, username);
}
@Override
public int getId() {
return 0x02;
}
}

View File

@ -0,0 +1,20 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class SpawnPositionPacket implements ServerPacket {
public int x, y, z;
@Override
public void write(Buffer buffer) {
Utils.writePosition(buffer, x, y, z);
}
@Override
public int getId() {
return 0x49;
}
}

View File

@ -0,0 +1,25 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
public class WindowItemsPacket implements ServerPacket {
public byte windowId;
public short count;
// TODO slot data (Array of Slot)
@Override
public void write(Buffer buffer) {
buffer.putByte(windowId);
buffer.putShort(count);
// TODO replace with actual array of slot
buffer.putBoolean(false); // Not present
}
@Override
public int getId() {
return 0x15;
}
}

View File

@ -0,0 +1,23 @@
package fr.themode.minestom.net.packet.server.status;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
public class PongPacket implements ServerPacket {
public long number;
public PongPacket(long number) {
this.number = number;
}
@Override
public void write(Buffer buffer) {
buffer.putLong(number);
}
@Override
public int getId() {
return 0x01;
}
}

View File

@ -0,0 +1,41 @@
package fr.themode.minestom.net.player;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.server.ServerPacket;
import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER;
public class PlayerConnection {
private Connection connection;
private ConnectionState connectionState;
public PlayerConnection(Connection connection) {
this.connection = connection;
this.connectionState = ConnectionState.UNKNOWN;
}
public void sendPacket(ServerPacket serverPacket) {
Packet packet = Packet.create();
Buffer buffer = packet.getPayload();
serverPacket.write(buffer);
packet.put(PACKET_ID_IDENTIFIER, serverPacket.getId());
this.connection.sendPacket(packet);
}
public Connection getConnection() {
return connection;
}
public void setConnectionState(ConnectionState connectionState) {
this.connectionState = connectionState;
}
public ConnectionState getConnectionState() {
return connectionState;
}
}

View File

@ -0,0 +1,67 @@
package fr.themode.minestom.net.protocol;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.protocol.Protocol;
import java.util.ArrayList;
import java.util.Collection;
import static fr.themode.minestom.utils.Utils.readVarInt;
import static fr.themode.minestom.utils.Utils.writeVarInt;
public class MinecraftProtocol extends Protocol {
public static final String PACKET_ID_IDENTIFIER = "id";
public MinecraftProtocol() {
super("minecraft");
}
@Override
public boolean verify(Buffer buffer) {
int length = readVarInt(buffer);
int realLength = buffer.slice(buffer.readerIndex()).length();
int id = readVarInt(buffer);
buffer.readerIndex(0);
return length == realLength && id >= 0;
}
@Override
public boolean verify(Packet packet) {
return packet.get("id") != null;
}
@Override
public Collection<Buffer> cut(Buffer buffer) {
ArrayList<Buffer> buffers = new ArrayList<>();
int read = 0;
while (read < buffer.length()) {
int lengthLength = buffer.readerIndex(read).readerIndex();
int length = readVarInt(buffer);
lengthLength = buffer.readerIndex() - lengthLength;
buffers.add(buffer.sliceCopy(read, length + lengthLength));
read += length + lengthLength;
}
return buffers;
}
@Override
public Packet decode(Buffer buffer) {
int length = readVarInt(buffer);
int id = readVarInt(buffer);
Buffer packetPayload = buffer.sliceCopy(buffer.readerIndex());
return Packet.create(packetPayload).put(PACKET_ID_IDENTIFIER, id);
}
@Override
public Buffer encode(Packet packet) {
Buffer buffer = Buffer.create();
Buffer idAndPayload = Buffer.create();
writeVarInt(idAndPayload, packet.get(PACKET_ID_IDENTIFIER));
idAndPayload.putBuffer(packet.getPayload());
writeVarInt(buffer, idAndPayload.length());
buffer.putBuffer(idAndPayload);
return buffer;
}
}

View File

@ -0,0 +1,111 @@
package fr.themode.minestom.utils;
import fr.adamaq01.ozao.net.Buffer;
import java.io.UnsupportedEncodingException;
public class Utils {
public static void writeString(Buffer buffer, String value) {
byte[] bytes = new byte[0];
try {
bytes = value.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (bytes.length > 32767) {
System.out.println("String too big (was " + value.length() + " bytes encoded, max " + 32767 + ")");
} else {
writeVarInt(buffer, bytes.length);
buffer.putBytes(bytes);
}
}
public static String readString(Buffer buffer) {
int length = readVarInt(buffer);
byte bytes[] = buffer.getBytes(length);
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public static void writeVarInt(Buffer buffer, int value) {
do {
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
buffer.putByte(temp);
} while (value != 0);
}
public static int readVarInt(Buffer buffer) {
int numRead = 0;
int result = 0;
byte read;
do {
read = buffer.getByte();
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 int lengthVarInt(int value) {
int i = 0;
do {
i++;
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
} while (value != 0);
return i;
}
public static void writeVarLong(Buffer buffer, long value) {
do {
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
buffer.putByte(temp);
} while (value != 0);
}
public static long readVarLong(Buffer buffer) {
int numRead = 0;
long result = 0;
byte read;
do {
read = buffer.getByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 10) {
throw new RuntimeException("VarLong is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
public static void writePosition(Buffer buffer, int x, int y, int z) {
buffer.putLong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF));
}
}

View File

@ -0,0 +1,16 @@
package fr.themode.minestom.world;
public enum Difficulty {
PEACEFUL((byte) 0), EASY((byte) 1), NORMAL((byte) 2), HARD((byte) 3);
private byte id;
Difficulty(byte id) {
this.id = id;
}
public byte getId() {
return id;
}
}

View File

@ -0,0 +1,16 @@
package fr.themode.minestom.world;
public enum Dimension {
NETHER(-1), OVERWORLD(0), END(1);
private int id;
Dimension(int id) {
this.id = id;
}
public int getId() {
return id;
}
}