Archived
0

Ping server

This commit is contained in:
2018-06-10 20:04:33 +03:00
parent 4a5f5cf721
commit 796076c97c
18 changed files with 825 additions and 1 deletions

View File

@@ -19,6 +19,8 @@ public abstract class NetStream {
public abstract int readUnsignedShort();
public abstract short readShort();
public abstract int readInt();
public abstract int readVarInt();
public abstract long readLong();
public abstract float readFloat();
public abstract double readDouble();
public abstract String readString();
@@ -28,6 +30,7 @@ public abstract class NetStream {
public abstract void writeBytes(byte[] buffer);
public abstract void writeShort(int value);
public abstract void writeInt(int value);
public abstract void writeVarInt(int value);
public abstract void writeLong(long value);
public abstract void writeFloat(float value);
public abstract void writeDouble(double value);

View File

@@ -37,4 +37,19 @@ public abstract class NetStream_p125 extends NetStream {
writeBytes(buf);
}
}
@Override
public int readVarInt() {
return readInt();
}
@Override
public void writeVarInt(int value) {
writeInt(value);
}
@Override
public long readLong() {
return 0; //FIXME
}
}

View File

@@ -23,7 +23,9 @@ import org.springframework.context.ApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class NettyServer implements Server {
@@ -56,7 +58,10 @@ public class NettyServer implements Server {
@Override
protected void initChannel(SocketChannel socketChannel) {
Map<String, ChannelHandler> beans = applicationContext.getBeansOfType(ChannelHandler.class);
beans.forEach(socketChannel.pipeline()::addLast);
beans.entrySet().stream()
.sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
.forEach(socketChannel.pipeline()::addLast);
}
};
}

11
proto_1.12.2/build.gradle Normal file
View File

@@ -0,0 +1,11 @@
group 'mc'
version '1.0-SNAPSHOT'
dependencies {
/* Core */
compile_excludeCopy project(':core')
/* Components */
compile (group: 'com.google.guava', name: 'guava', version: '24.1-jre')
compile (group: 'com.google.code.gson', name: 'gson', version: '2.8.2')
}

View File

@@ -0,0 +1,121 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2;
import java.io.ByteArrayOutputStream;
public class ByteArrayOutputNetStream extends NetStream_p340 {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
@Override
public boolean readBoolean() {
throw new UnsupportedOperationException();
}
@Override
public byte readByte() {
throw new UnsupportedOperationException();
}
@Override
public void readBytes(byte[] buffer) {
throw new UnsupportedOperationException();
}
@Override
public int readUnsignedByte() {
throw new UnsupportedOperationException();
}
@Override
public int readUnsignedShort() {
throw new UnsupportedOperationException();
}
@Override
public short readShort() {
throw new UnsupportedOperationException();
}
@Override
public int readInt() {
throw new UnsupportedOperationException();
}
@Override
public long readLong() {
throw new UnsupportedOperationException();
}
@Override
public float readFloat() {
throw new UnsupportedOperationException();
}
@Override
public double readDouble() {
throw new UnsupportedOperationException();
}
@Override
public void writeBoolean(boolean value) {
baos.write(value ? 1 : 0);
}
@Override
public void writeByte(int value) {
baos.write(value);
}
@Override
public void writeBytes(byte[] buffer) {
baos.write(buffer, 0, buffer.length);
}
@Override
public void writeShort(int value) {
baos.write((byte) value >>> 8);
baos.write((byte) value);
}
@Override
public void writeInt(int value) {
baos.write((byte)((int)(value >>> 24)));
baos.write((byte)((int)(value >>> 16)));
baos.write((byte)((int)(value >>> 8)));
baos.write((byte)((int)(value)));
}
@Override
public void writeLong(long value) {
baos.write((byte)((int)(value >>> 56)));
baos.write((byte)((int)(value >>> 48)));
baos.write((byte)((int)(value >>> 40)));
baos.write((byte)((int)(value >>> 32)));
baos.write((byte)((int)(value >>> 24)));
baos.write((byte)((int)(value >>> 16)));
baos.write((byte)((int)(value >>> 8)));
baos.write((byte)((int)(value)));
}
@Override
public void writeFloat(float value) {
writeInt(Float.floatToIntBits(value));
}
@Override
public void writeDouble(double value) {
writeLong(Double.doubleToLongBits(value));
}
@Override
public void skipBytes(int count) {
throw new UnsupportedOperationException();
}
public byte[] toByteArray() {
return baos.toByteArray();
}
}

View File

@@ -0,0 +1,73 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetStream;
import java.nio.charset.StandardCharsets;
@Slf4j
public abstract class NetStream_p340 extends NetStream {
@Override
public int readVarInt() {
int numRead = 0;
int result = 0;
byte read;
do {
read = readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 5) {
log.warn("VarInt is too big");
break;
}
} while ((read & 0b10000000) != 0);
return result;
}
@Override
public void writeVarInt(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;
}
writeByte(temp);
} while (value != 0);
}
@Override
public String readString() {
int size = readVarInt();
if (size == 0) {
log.warn("String zero length??");
return "";
}
byte[] bytes = new byte[size];
readBytes(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
@Override
public void writeString(String value) {
if (value.length() > Short.MAX_VALUE) {
log.warn("String \"{}\" too long!", value);
byte[] buf = value.substring(0, Short.MAX_VALUE).getBytes(StandardCharsets.UTF_8);
writeVarInt(Short.MAX_VALUE);
writeBytes(buf);
} else {
byte[] buf = value.getBytes(StandardCharsets.UTF_8);
writeVarInt(value.length());
writeBytes(buf);
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-08
*/
package mc.core.network.proto_1_12_2;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.packets.HandshakePacket;
import mc.core.network.proto_1_12_2.packets.PingPacket;
import mc.core.network.proto_1_12_2.packets.StatusRequestPacket;
import mc.core.network.proto_1_12_2.packets.StatusResponsePacket;
@Slf4j
@RequiredArgsConstructor
public enum State {
UNKNOWN(-1, null, null),
HANDSHAKE(0,
ImmutableBiMap.<Integer, Class<? extends CSPacket>>builder()
.put(0, HandshakePacket.class)
.build(),
null
),
STATUS(1,
ImmutableBiMap.<Integer, Class<? extends CSPacket>>builder()
.put(0, StatusRequestPacket.class)
.put(1, PingPacket.class)
.build(),
ImmutableBiMap.<Class<? extends SCPacket>, Integer>builder()
.put(StatusResponsePacket.class, 0)
.put(PingPacket.class, 1)
.build()
);
public static State valueOf(int id) {
if (id == 0) return HANDSHAKE;
else if (id == 1) return STATUS;
else {
log.warn("Unknown state: {}", id);
return UNKNOWN;
}
}
@Getter
private final int id;
private final BiMap<Integer, Class<? extends CSPacket>> clientSidePacketsMap;
private final BiMap<Class<? extends SCPacket>, Integer> serverSidePacketsMap;
public Class<? extends CSPacket> getClientSidePacket(int id) {
return clientSidePacketsMap.get(id);
}
public Integer getServerSidePacket(Class<? extends SCPacket> clazz) {
return serverSidePacketsMap.get(clazz);
}
}

View File

@@ -0,0 +1,30 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetStream;
import mc.core.network.proto_1_12_2.State;
@NoArgsConstructor
@Getter
@ToString
public class HandshakePacket implements CSPacket {
private int protocolVersion;
private String address;
private int serverPort;
private State nextState;
@Override
public void readSelf(NetStream netStream) {
this.protocolVersion = netStream.readVarInt();
this.address = netStream.readString();
this.serverPort = netStream.readUnsignedShort();
this.nextState = State.valueOf(netStream.readVarInt());
}
}

View File

@@ -0,0 +1,28 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
@ToString
public class PingPacket implements CSPacket, SCPacket {
private long payload;
@Override
public void readSelf(NetStream netStream) {
this.payload = netStream.readLong();
}
@Override
public byte[] toByteArray() {
ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
netStream.writeLong(payload);
return netStream.toByteArray();
}
}

View File

@@ -0,0 +1,16 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetStream;
@ToString
public class StatusRequestPacket implements CSPacket {
@Override
public void readSelf(NetStream netStream) {
}
}

View File

@@ -0,0 +1,54 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import com.google.gson.JsonObject;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
@Setter
@ToString
public class StatusResponsePacket implements SCPacket {
private static final JsonObject versionObj;
static {
versionObj = new JsonObject();
versionObj.addProperty("name", "1.12.2");
versionObj.addProperty("protocol", 340);
}
private int maxOnline;
private int online;
private String description;
private byte[] faviconBase64;
@Override
public byte[] toByteArray() {
ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
JsonObject playersObj = new JsonObject();
playersObj.addProperty("max", maxOnline);
playersObj.addProperty("online", online);
JsonObject descriptionObj = new JsonObject();
descriptionObj.addProperty("text", description);
JsonObject rootObj = new JsonObject();
rootObj.add("version", versionObj);
rootObj.add("players", playersObj);
rootObj.add("description", descriptionObj);
if (faviconBase64 != null && faviconBase64.length > 0) {
rootObj.addProperty("favicon",
"data:image/png;base64," + new String(faviconBase64)
);
}
netStream.writeString(rootObj.toString());
return netStream.toByteArray();
}
}

View File

@@ -0,0 +1,14 @@
group 'mc'
version '1.0-SNAPSHOT'
ext {
netty_version = '4.1.22.Final'
}
dependencies {
/* Protocol 1.12.2 */
compile_excludeCopy project(':proto_1.12.2')
/* Netty */
compile (group: 'io.netty', name: 'netty-all', version: netty_version)
}

View File

@@ -0,0 +1,81 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.Server;
import mc.core.network.StartServerException;
import mc.core.network.proto_1_12_2.State;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.util.Map;
@Slf4j
public class NettyServer implements Server {
static final AttributeKey<State> ATTR_STATE = AttributeKey.newInstance("ATTR_STATE");
@Autowired
private ApplicationContext applicationContext;
@Setter
private String host;
@Setter
private int port;
@Setter
private int workerGroupCount = 0;
private EventLoopGroup bossGroup, workerGroup;
private ChannelInitializer buildChannelInitializer() {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
Map<String, ChannelHandler> beans = applicationContext.getBeansOfType(ChannelHandler.class);
beans.forEach(socketChannel.pipeline()::addLast);
}
};
}
private ServerBootstrap buildServerBootstrap() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(buildChannelInitializer());
return bootstrap;
}
@Override
public void start() throws StartServerException {
log.info("Use protocol 1.7.10");
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(workerGroupCount);
ServerBootstrap serverBootstrap = buildServerBootstrap();
log.info("Start server: {}:{}", host, port);
try {
serverBootstrap.bind(host, port).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new StartServerException(e);
}
}
@Override
public void stop() {
log.info("Server shutdown");
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

View File

@@ -0,0 +1,60 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import mc.core.network.NetStream;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetStream;
import java.util.List;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
public class PacketDecoder extends ByteToMessageDecoder {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(ATTR_STATE).set(State.HANDSHAKE);
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(ATTR_STATE).set(null);
ctx.fireChannelInactive();
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
log.debug("ByteBuf readableBytes: {}", in.readableBytes());
State state = ctx.channel().attr(ATTR_STATE).get();
NetStream netStream = new WrapperNetStream(in);
int packetSize = netStream.readVarInt();
log.debug("Packet size: {}", packetSize);
int rb = in.readableBytes();
int packetId = netStream.readVarInt();
log.debug("Packet id: {}", packetId);
rb = rb - in.readableBytes();
Class<? extends CSPacket> packetClass = state.getClientSidePacket(packetId);
if (packetClass == null) {
log.warn("Unknown packet: {}:{}", state.name(), packetId);
in.skipBytes(packetSize - rb);
} else {
CSPacket packet = packetClass.newInstance();
packet.readSelf(netStream);
log.debug("Known packet: {}:{}", state.name(), packet.toString());
out.add(packet);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetStream;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
public class PacketEncoder extends MessageToByteEncoder<SCPacket> {
private static int sizeVarInt(final int value) {
byte size = 0;
int v = value;
do {
v >>>= 7;
size++;
} while (v != 0);
return size;
}
@Override
protected void encode(ChannelHandlerContext ctx, SCPacket packet, ByteBuf out) throws Exception {
State state = ctx.channel().attr(ATTR_STATE).get();
Integer id = state.getServerSidePacket(packet.getClass());
if (id == null) {
log.error("Not defined ID packet: {}:{}", state.name(), packet.getClass());
return;
}
byte[] bytes = packet.toByteArray();
NetStream netStream = new WrapperNetStream(out);
netStream.writeVarInt(bytes.length + sizeVarInt(id));
netStream.writeVarInt(id);
netStream.writeBytes(bytes);
}
}

View File

@@ -0,0 +1,91 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import mc.core.Config;
import mc.core.chat.ChatProcessor;
import mc.core.network.CSPacket;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.packets.HandshakePacket;
import mc.core.network.proto_1_12_2.packets.PingPacket;
import mc.core.network.proto_1_12_2.packets.StatusRequestPacket;
import mc.core.network.proto_1_12_2.packets.StatusResponsePacket;
import mc.core.player.Player;
import mc.core.player.PlayerManager;
import mc.core.world.World;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
public class PacketHandler extends SimpleChannelInboundHandler<CSPacket> {
private static final AttributeKey<Player> ATTR_PLAYER = AttributeKey.newInstance("ATTR_PLAYER");
@Autowired
private Config config;
@Autowired
private PlayerManager playerManager;
@Autowired
private World world;
@Autowired
private ChatProcessor chatProcessor;
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
Player player = ctx.channel().attr(ATTR_PLAYER).get();
if (player != null) {
playerManager.leftServer(player);
player.setChannel(null);
}
ctx.channel().attr(ATTR_PLAYER).set(null);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, CSPacket packet) throws Exception {
Optional<Method> optionalMethod = Arrays.stream(this.getClass().getDeclaredMethods())
.filter(method -> method.getName().equals("on" + packet.getClass().getSimpleName().replace("Packet", ""))
&& method.getParameterCount() == 2
&& method.getParameterTypes()[0].isAssignableFrom(Channel.class)
&& method.getParameterTypes()[1].isAssignableFrom(packet.getClass()))
.findFirst();
if (optionalMethod.isPresent()) {
Method method = optionalMethod.get();
method.invoke(this, ctx.channel(), packet);
}
}
private void onHandshake(Channel channel, HandshakePacket packet) {
if (packet.getNextState().equals(State.UNKNOWN)) return;
channel.attr(ATTR_STATE).set(packet.getNextState());
}
private void onStatusRequest(Channel channel, StatusRequestPacket packet) {
if (!channel.attr(ATTR_STATE).get().equals(State.STATUS)) return;
StatusResponsePacket responsePacket = new StatusResponsePacket();
responsePacket.setMaxOnline(config.getMaxPlayers());
responsePacket.setDescription(config.getDescriptionServer());
responsePacket.setFaviconBase64(config.getFaviconBase64());
responsePacket.setOnline(playerManager.getCountOnlinePlayers());
channel.writeAndFlush(responsePacket);
}
private void onPing(Channel channel, PingPacket packet) {
if (!channel.attr(ATTR_STATE).get().equals(State.STATUS)) return;
channel.writeAndFlush(packet);
}
}

View File

@@ -0,0 +1,111 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-04-08
*/
package mc.core.network.proto_1_12_2.netty.wrappers;
import io.netty.buffer.ByteBuf;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.proto_1_12_2.NetStream_p340;
@Slf4j
@RequiredArgsConstructor
public class WrapperNetStream extends NetStream_p340 {
private final ByteBuf byteBuf;
@Override
public boolean readBoolean() {
return byteBuf.readBoolean();
}
@Override
public byte readByte() {
return byteBuf.readByte();
}
@Override
public void readBytes(byte[] buffer) {
byteBuf.readBytes(buffer);
}
@Override
public int readUnsignedByte() {
return byteBuf.readUnsignedByte();
}
@Override
public int readUnsignedShort() {
return byteBuf.readUnsignedShort();
}
@Override
public short readShort() {
return byteBuf.readShort();
}
@Override
public int readInt() {
return byteBuf.readInt();
}
@Override
public long readLong() {
return byteBuf.readLong();
}
@Override
public float readFloat() {
return byteBuf.readFloat();
}
@Override
public double readDouble() {
return byteBuf.readDouble();
}
@Override
public void writeBoolean(boolean value) {
byteBuf.writeBoolean(value);
}
@Override
public void writeByte(int value) {
byteBuf.writeByte(value);
}
@Override
public void writeBytes(byte[] buffer) {
byteBuf.writeBytes(buffer);
}
@Override
public void writeShort(int value) {
byteBuf.writeShort(value);
}
@Override
public void writeInt(int value) {
byteBuf.writeInt(value);
}
@Override
public void writeLong(long value) {
byteBuf.writeLong(value);
}
@Override
public void writeFloat(float value) {
byteBuf.writeFloat(value);
}
@Override
public void writeDouble(double value) {
byteBuf.writeDouble(value);
}
@Override
public void skipBytes(int count) {
byteBuf.skipBytes(count);
}
}

View File

@@ -5,3 +5,5 @@ include('proto125') // Protocol 1.2.5
include('proto125_netty') // Protocol 1.2.5 (Netty impl.)
include('flat_world')
include('vanilla_commands')
include('proto_1.12.2') // Protocol 1.12.2
include('proto_1.12.2_netty') // Protocol 1.12.2 (Netty impl.)