diff --git a/pom.xml b/pom.xml index f9a195c..3864046 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ 1.8 1.7.21 2.5 + 4.2.5.RELEASE 4.1.22.Final @@ -24,6 +25,11 @@ slf4j-api ${slf4j.version} + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + org.apache.logging.log4j log4j-core @@ -35,28 +41,18 @@ ${log4j.version} - + - org.projectlombok - lombok - 1.16.16 + org.springframework + spring-context + ${spring.version} + + + commons-logging + commons-logging + + - - com.google.code.gson - gson - 2.8.2 - - - com.google.guava - guava - 24.1-jre - - - commons-io - commons-io - 2.6 - - @@ -65,6 +61,27 @@ ${netty.version} + + + org.projectlombok + lombok + 1.16.16 + + + commons-io + commons-io + 2.6 + + + com.google.guava + guava + 24.1-jre + + + com.google.code.gson + gson + 2.8.2 + diff --git a/src/main/java/mc/core/ByteArrayOutputNetStream.java b/src/main/java/mc/core/ByteArrayOutputNetStream.java new file mode 100644 index 0000000..03e2902 --- /dev/null +++ b/src/main/java/mc/core/ByteArrayOutputNetStream.java @@ -0,0 +1,40 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core; + +import java.io.ByteArrayOutputStream; + +public class ByteArrayOutputNetStream extends NetStream { + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + @Override + public byte readByte() { + throw new UnsupportedOperationException(); + } + + @Override + public void readBytes(byte[] buffer) { + throw new UnsupportedOperationException(); + } + + @Override + public int readUnsignedShort() { + throw new UnsupportedOperationException(); + } + + @Override + public void writeByte(int value) { + baos.write(value); + } + + @Override + public void writeBytes(byte[] buffer) { + baos.write(buffer, 0, buffer.length); + } + + public byte[] toByteArray() { + return baos.toByteArray(); + } +} diff --git a/src/main/java/mc/core/CSPacket.java b/src/main/java/mc/core/CSPacket.java new file mode 100644 index 0000000..319ba81 --- /dev/null +++ b/src/main/java/mc/core/CSPacket.java @@ -0,0 +1,10 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core; + +public interface CSPacket { + default void readSelf(NetStream netStream) { + } +} diff --git a/src/main/java/mc/core/Config.java b/src/main/java/mc/core/Config.java new file mode 100644 index 0000000..247c2e0 --- /dev/null +++ b/src/main/java/mc/core/Config.java @@ -0,0 +1,13 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core; + +public interface Config { + int getMaxPlayers(); + String getDescriptionServer(); + byte[] getFaviconBase64(); + String getHost(); + int getPort(); +} diff --git a/src/main/java/mc/core/Main.java b/src/main/java/mc/core/Main.java index 096ddc8..440cf83 100644 --- a/src/main/java/mc/core/Main.java +++ b/src/main/java/mc/core/Main.java @@ -5,18 +5,22 @@ package mc.core; import lombok.extern.slf4j.Slf4j; -import mc.core.netty.NettyServer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; @Slf4j public class Main { + public static ApplicationContext appContext; //FIXME + public static void main(String[] args) { - log.info("Start server"); - Server server = new NettyServer(); + appContext = new ClassPathXmlApplicationContext("spring.xml"); + Config config = appContext.getBean("config", Config.class); + + Server server = appContext.getBean("server", Server.class); try { - server.start("127.0.0.1", 25565); + server.start(config.getHost(), config.getPort()); } catch (StartServerException e) { - log.error("Starting server error", e); - server.stop(); + log.error("Can't start server", e); } } } diff --git a/src/main/java/mc/core/NetStream.java b/src/main/java/mc/core/NetStream.java index 8c2d63b..5305236 100644 --- a/src/main/java/mc/core/NetStream.java +++ b/src/main/java/mc/core/NetStream.java @@ -4,29 +4,82 @@ */ package mc.core; -public interface NetStream { - byte readByte(); - void writeByte(byte value); +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; - int readBytes(byte[] buffer); - void writeBytes(byte[] buffer); +import java.nio.charset.StandardCharsets; - int readBytes(byte[] buffer, int offset, int length); - void writeBytes(byte[] buffer, int offset, int length); +@Slf4j +public abstract class NetStream { + @Getter + @Setter + private int expectedSize; - int readVarInt(); - void writeVarInt(int value); + public static int sizeVarInt(final int value) { + byte size = 0; + int v = value; - long readVarLong(); - void writeVarLong(long value); + do { + v >>>= 7; + size++; + } while (v != 0); - String readString(); - void writeString(String value); + return size; + } - int readUnsignedShort(); + public int readVarInt() { + int result = 0; + byte read; + byte numRead = 0; + + do { + read = readByte(); + int value = (read & 0b01111111); + result |= (value << (7 * numRead)); + + numRead++; + if (numRead > 5) { + log.debug("VarInt is too big!"); + break; + } + } while ((read & 0b10000000) != 0); + + return result; + } + + public void writeVarInt(int value) { + do { + byte temp = (byte)(value & 0b01111111); + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + + writeByte(temp); + } while (value != 0); + } + + public String readString() { + int length = readVarInt(); + + byte[] buffer = new byte[length]; + readBytes(buffer); + + return new String(buffer, StandardCharsets.UTF_8); + } + + public void writeString(String value) { + writeVarInt(value.length()); + writeBytes(value.getBytes()); + } + + public abstract byte readByte(); + public abstract void readBytes(byte[] buffer); + public abstract int readUnsignedShort(); + + public abstract void writeByte(int value); + public abstract void writeBytes(byte[] buffer); - void skipBytes(int count); - void setExpectedLength(int value); - int getExpectedLength(); } diff --git a/src/main/java/mc/core/SCPacket.java b/src/main/java/mc/core/SCPacket.java new file mode 100644 index 0000000..161b47e --- /dev/null +++ b/src/main/java/mc/core/SCPacket.java @@ -0,0 +1,9 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core; + +public interface SCPacket { + byte[] toByteArray(); +} diff --git a/src/main/java/mc/core/Server.java b/src/main/java/mc/core/Server.java index b98120e..19ee13b 100644 --- a/src/main/java/mc/core/Server.java +++ b/src/main/java/mc/core/Server.java @@ -6,6 +6,4 @@ package mc.core; public interface Server { void start(String host, int port) throws StartServerException; - void stop(); - boolean isRunning(); } diff --git a/src/main/java/mc/core/embedded/ConfigFromSpring.java b/src/main/java/mc/core/embedded/ConfigFromSpring.java new file mode 100644 index 0000000..215f77a --- /dev/null +++ b/src/main/java/mc/core/embedded/ConfigFromSpring.java @@ -0,0 +1,41 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.embedded; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import mc.core.Config; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Base64; + +@Slf4j +@Getter +public class ConfigFromSpring implements Config { + @Setter + private String descriptionServer; + private byte[] faviconBase64; + @Setter + private int maxPlayers; + @Setter + private String host; + @Setter + private int port; + + public void setFaviconBase64(File faviconImageFile) { + log.debug("faviconImageFile: {}", faviconImageFile.getAbsolutePath()); + try { + faviconBase64 = Base64.getEncoder().encode( + FileUtils.readFileToByteArray(faviconImageFile) + ); + } catch (IOException e) { + log.warn("Can't load favicon", e); + faviconBase64 = null; + } + } +} diff --git a/src/main/java/mc/core/netty/NettyServer.java b/src/main/java/mc/core/netty/NettyServer.java index 4b192d0..74478d9 100644 --- a/src/main/java/mc/core/netty/NettyServer.java +++ b/src/main/java/mc/core/netty/NettyServer.java @@ -13,7 +13,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LoggingHandler; import mc.core.Server; import mc.core.StartServerException; -import mc.core.netty.handlers.HandshakeHandler; public class NettyServer implements Server { private EventLoopGroup bossGroup, workerGroup; @@ -49,25 +48,10 @@ public class NettyServer implements Server { ServerBootstrap serverBootstrap = buildServerBootstrap(); - EventBus.getInstance().listenIncomingPacket(new HandshakeHandler()); - try { serverBootstrap.bind(host, port).sync().channel().closeFuture().sync(); } catch (InterruptedException e) { throw new StartServerException(e); } } - - @Override - public void stop() { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } - - @Override - public boolean isRunning() { - return bossGroup != null - && workerGroup != null - && !(bossGroup.isShutdown() && workerGroup.isShutdown()); - } } diff --git a/src/main/java/mc/core/netty/PacketDecoder.java b/src/main/java/mc/core/netty/PacketDecoder.java index b56b1b4..7269146 100644 --- a/src/main/java/mc/core/netty/PacketDecoder.java +++ b/src/main/java/mc/core/netty/PacketDecoder.java @@ -6,44 +6,65 @@ package mc.core.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; +import io.netty.handler.codec.ByteToMessageDecoder; import lombok.extern.slf4j.Slf4j; +import mc.core.CSPacket; import mc.core.NetStream; -import mc.core.Packet; +import mc.core.netty.packets.RawPacket; import java.util.List; -import java.util.Optional; @Slf4j -public class PacketDecoder extends ReplayingDecoder { +public class PacketDecoder extends ByteToMessageDecoder { @Override - protected void decode(ChannelHandlerContext context, ByteBuf byteBuf, List list) throws Exception { - NetStream netStream = new WrapperByteBufNetStream(byteBuf); + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(State.ATTR_STATE).set(State.STATUS); + ctx.fireChannelActive(); + } - int length = netStream.readVarInt(); - netStream.setExpectedLength(length); + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(State.ATTR_STATE).set(null); + ctx.fireChannelInactive(); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + State state = ctx.channel().attr(State.ATTR_STATE).get(); + NetStream netStream = new WrapperNetStream(in); + + log.debug("ByteBuf readableBytes: {}", in.readableBytes()); + + int size = netStream.readVarInt(); + log.debug("Pkt-Size: {}", size); int id = netStream.readVarInt(); - log.debug("PktLEN: {} | PktID: {}", length, id); + log.debug("Pkt-Id: {}", id); - State state = context.channel().attr(State.ATTR_STATE).get(); - log.debug("Current state: {}:{}", state.getId(), state.name()); - Optional> packetClass = state.getPacketClass(id); + Class clientSidePacketClass = state.getClientSidePacket(id); + if (clientSidePacketClass == null) { + log.warn("Unknown packet: {}:{}", state.name(), id); - if (packetClass.isPresent()) { - log.debug("Detect packet: {}", packetClass.get().getSimpleName()); - Packet packet = packetClass.get().newInstance(); - packet.readSelf(netStream); - list.add(packet); - if (length < packet.getSize()) { - log.warn("WTF?! length < packet.getSize() !!"); - log.warn("Packet size: {}", packet.getSize()); - } else if (length > packet.getSize()) { - log.debug("skipBytes {}", length - packet.getSize()); - byteBuf.skipBytes(length - packet.getSize()); + if (log.isDebugEnabled()) { + byte[] rawData; + if (size > in.readableBytes()) { + rawData = new byte[in.readableBytes()]; + } else { + rawData = new byte[size - NetStream.sizeVarInt(id)]; + } + in.readBytes(rawData); + + RawPacket packet = new RawPacket(); + packet.setRawData(rawData); + out.add(packet); } } else { - list.add(new UnknownPacket(length, id)); - byteBuf.skipBytes(length-1); + CSPacket packet = clientSidePacketClass.newInstance(); + netStream.setExpectedSize(size - NetStream.sizeVarInt(id)); + packet.readSelf(netStream); + out.add(packet); } + + log.debug("ByteBuf readableBytes: {}", in.readableBytes()); + in.skipBytes(in.readableBytes()); } } diff --git a/src/main/java/mc/core/netty/PacketEncoder.java b/src/main/java/mc/core/netty/PacketEncoder.java index 9c241c1..cedfb64 100644 --- a/src/main/java/mc/core/netty/PacketEncoder.java +++ b/src/main/java/mc/core/netty/PacketEncoder.java @@ -7,11 +7,26 @@ package mc.core.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import mc.core.Packet; +import lombok.extern.slf4j.Slf4j; +import mc.core.NetStream; +import mc.core.SCPacket; -public class PacketEncoder extends MessageToByteEncoder { +@Slf4j +public class PacketEncoder extends MessageToByteEncoder { @Override - protected void encode(ChannelHandlerContext context, Packet packet, ByteBuf byteBuf) throws Exception { - packet.writeSelf(new WrapperByteBufNetStream(byteBuf)); + protected void encode(ChannelHandlerContext ctx, SCPacket pkt, ByteBuf out) throws Exception { + State state = ctx.channel().attr(State.ATTR_STATE).get(); + Integer id = state.getServerSidePaclet(pkt.getClass()); + if (id == null) { + log.error("Packet not found: {}:{}", state.name(), pkt.getClass().getSimpleName()); + return; + } + + byte[] bytes = pkt.toByteArray(); + NetStream netStream = new WrapperNetStream(out); + + netStream.writeVarInt(bytes.length + NetStream.sizeVarInt(id)); + netStream.writeVarInt(id); + netStream.writeBytes(bytes); } } diff --git a/src/main/java/mc/core/netty/PacketHandler.java b/src/main/java/mc/core/netty/PacketHandler.java index 56ed584..5134fc7 100644 --- a/src/main/java/mc/core/netty/PacketHandler.java +++ b/src/main/java/mc/core/netty/PacketHandler.java @@ -4,32 +4,58 @@ */ package mc.core.netty; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import mc.core.Packet; -import mc.core.netty.packets.HandshakeRequestPacket; -import mc.core.netty.packets.HandshakeResponsePacket; +import mc.core.CSPacket; +import mc.core.Config; +import mc.core.Main; +import mc.core.netty.packets.PingPacket; +import mc.core.netty.packets.StatusRequest; +import mc.core.netty.packets.StatusResponse; -import static mc.core.netty.Utils.equalsPacket; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Optional; @Slf4j -public class PacketHandler extends SimpleChannelInboundHandler { +public class PacketHandler extends SimpleChannelInboundHandler { @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - context.channel().attr(State.ATTR_STATE).set(State.Handshaking); + protected void channelRead0(ChannelHandlerContext ctx, CSPacket packet) throws Exception { + log.debug("{}: {}", packet.getClass().getSimpleName(), packet.toString()); + + Optional optionalMethod = Arrays.stream(this.getClass().getDeclaredMethods()) + .filter(method -> method.getName().equals("on" + packet.getClass().getSimpleName()) + && 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); + } } - @Override - protected void channelRead0(ChannelHandlerContext context, Packet packet) throws Exception { - log.info("Packet: {} | Data: {}", - packet.getClass().getSimpleName(), - packet.toString()); + public void onStatusRequest(Channel channel, StatusRequest packet) { + if (!packet.getNextState().equals(State.UNKNOWN)) { + channel.attr(State.ATTR_STATE).set(packet.getNextState()); + } - EventBus.getInstance().eventIncomingPacket( - context.channel().attr(State.ATTR_STATE).get().getId(), - packet, - context.channel() - ); + if (packet.getNextState().equals(State.STATUS)) { + Config config = Main.appContext.getBean("config", Config.class); //FIXME + StatusResponse pkt = new StatusResponse(); + pkt.setDescription(config.getDescriptionServer()); + pkt.setMaxOnline(config.getMaxPlayers()); + pkt.setFaviconBase64(config.getFaviconBase64()); + + channel.writeAndFlush(pkt); + } + } + + public void onPingPacket(Channel channel, PingPacket packet) { + channel.writeAndFlush(packet); } } diff --git a/src/main/java/mc/core/netty/State.java b/src/main/java/mc/core/netty/State.java index 35f0d39..bbea653 100644 --- a/src/main/java/mc/core/netty/State.java +++ b/src/main/java/mc/core/netty/State.java @@ -4,45 +4,52 @@ */ package mc.core.netty; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableMap; import io.netty.util.AttributeKey; -import lombok.Getter; -import mc.core.Packet; -import mc.core.netty.packets.HandshakeRequestPacket; +import lombok.RequiredArgsConstructor; +import mc.core.CSPacket; +import mc.core.SCPacket; import mc.core.netty.packets.PingPacket; +import mc.core.netty.packets.StatusRequest; +import mc.core.netty.packets.StatusResponse; import java.util.Arrays; +import java.util.Map; import java.util.Optional; +@RequiredArgsConstructor public enum State { - Unknown(0, ImmutableBiMap.of()), - Handshaking(1, ImmutableBiMap.of( - 0, HandshakeRequestPacket.class, + UNKNOWN(0, ImmutableMap.of(), ImmutableMap.of()), + STATUS(1, + ImmutableMap.of( + 0, StatusRequest.class, 1, PingPacket.class - )); - //Login(2, ...) + ), + ImmutableMap.of( + StatusResponse.class, 0, + PingPacket.class, 1 + ) + ); public static final AttributeKey ATTR_STATE = AttributeKey.newInstance("ATTR_STATE"); - public static Optional getById(final int id) { - return Arrays.stream(State.values()).filter(state -> state.id == id).findFirst(); + public static State getStateById(final int id) { + Optional optionalState = Arrays.stream(State.values()) + .filter(state -> state.id == id) + .findFirst(); + + return optionalState.orElse(UNKNOWN); } - @Getter private final int id; - private final BiMap> requestMap; + private final Map> clientSidePackets; + private final Map, Integer> serverSidePackets; - State(int id, BiMap> requestMap) { - this.id = id; - this.requestMap = requestMap; + public Class getClientSidePacket(final int packetId) { + return clientSidePackets.get(packetId); } - public Optional> getPacketClass(int id) { - return Optional.ofNullable(requestMap.get(id)); - } - - public Optional getPacketId(Class clazz) { - return Optional.ofNullable(requestMap.inverse().get(clazz)); + public Integer getServerSidePaclet(final Class clazz) { + return serverSidePackets.get(clazz); } } diff --git a/src/main/java/mc/core/netty/WrapperNetStream.java b/src/main/java/mc/core/netty/WrapperNetStream.java new file mode 100644 index 0000000..34039da --- /dev/null +++ b/src/main/java/mc/core/netty/WrapperNetStream.java @@ -0,0 +1,39 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty; + +import io.netty.buffer.ByteBuf; +import lombok.RequiredArgsConstructor; +import mc.core.NetStream; + +@RequiredArgsConstructor +public class WrapperNetStream extends NetStream { + private final ByteBuf byteBuf; + + @Override + public byte readByte() { + return byteBuf.readByte(); + } + + @Override + public void readBytes(byte[] buffer) { + byteBuf.readBytes(buffer); + } + + @Override + public int readUnsignedShort() { + return byteBuf.readUnsignedShort(); + } + + @Override + public void writeByte(int value) { + byteBuf.writeByte(value); + } + + @Override + public void writeBytes(byte[] buffer) { + byteBuf.writeBytes(buffer); + } +} diff --git a/src/main/java/mc/core/netty/packets/PingPacket.java b/src/main/java/mc/core/netty/packets/PingPacket.java index 778c017..4ad21b3 100644 --- a/src/main/java/mc/core/netty/packets/PingPacket.java +++ b/src/main/java/mc/core/netty/packets/PingPacket.java @@ -4,39 +4,21 @@ */ package mc.core.netty.packets; -import lombok.ToString; +import mc.core.CSPacket; import mc.core.NetStream; -import mc.core.Packet; -import mc.core.netty.Utils; +import mc.core.SCPacket; -@ToString -public class PingPacket implements Packet { - private static final int id = 1; - private static final int lenId = Utils.lengthVarInt(id); - private byte[] rawdata; - private int size; - - @Override - public int getSize() { - return size; - } - - @Override - public int getId() { - return id; - } +public class PingPacket implements CSPacket, SCPacket { + private byte[] rawData; @Override public void readSelf(NetStream netStream) { - rawdata = new byte[netStream.getExpectedLength() - lenId]; - netStream.readBytes(rawdata); - size = lenId + rawdata.length; + rawData = new byte[netStream.getExpectedSize()]; + netStream.readBytes(rawData); } @Override - public void writeSelf(NetStream netStream) { - netStream.writeVarInt(size); - netStream.writeVarInt(id); - netStream.writeBytes(rawdata); + public byte[] toByteArray() { + return rawData; } } diff --git a/src/main/java/mc/core/netty/packets/RawPacket.java b/src/main/java/mc/core/netty/packets/RawPacket.java new file mode 100644 index 0000000..f8d1560 --- /dev/null +++ b/src/main/java/mc/core/netty/packets/RawPacket.java @@ -0,0 +1,17 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty.packets; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import mc.core.CSPacket; + +@ToString +public class RawPacket implements CSPacket { + @Getter + @Setter + private byte[] rawData; +} diff --git a/src/main/java/mc/core/netty/packets/StatusRequest.java b/src/main/java/mc/core/netty/packets/StatusRequest.java new file mode 100644 index 0000000..63437b1 --- /dev/null +++ b/src/main/java/mc/core/netty/packets/StatusRequest.java @@ -0,0 +1,35 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty.packets; + +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import mc.core.CSPacket; +import mc.core.NetStream; +import mc.core.netty.State; + +@Slf4j +@Getter +@ToString +public class StatusRequest implements CSPacket { + private int protocolVersion; + private String serverAddress; + private int serverPort; + private State nextState; + + @Override + public void readSelf(NetStream netStream) { + protocolVersion = netStream.readVarInt(); + serverAddress = netStream.readString(); + serverPort = netStream.readUnsignedShort(); + + int nextStateId = netStream.readVarInt(); + nextState = State.getStateById(nextStateId); + if (nextState.equals(State.UNKNOWN)){ + log.warn("Unknown state ({})!", nextStateId); + } + } +} diff --git a/src/main/java/mc/core/netty/packets/StatusResponse.java b/src/main/java/mc/core/netty/packets/StatusResponse.java new file mode 100644 index 0000000..a494914 --- /dev/null +++ b/src/main/java/mc/core/netty/packets/StatusResponse.java @@ -0,0 +1,56 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty.packets; + +import com.google.gson.JsonObject; +import lombok.Setter; +import lombok.ToString; +import mc.core.ByteArrayOutputNetStream; +import mc.core.SCPacket; + +@ToString +public class StatusResponse implements SCPacket { + private static final String name = "1.12.2"; + private static final int protocol = 340; + + @Setter + private int maxOnline; + @Setter + private int online; + @Setter + private String description; + @Setter + private byte[] faviconBase64; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + JsonObject versionObj = new JsonObject(); + versionObj.addProperty("name", name); + versionObj.addProperty("protocol", protocol); + + 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(); + } +} diff --git a/src/main/resources/spring.xml b/src/main/resources/spring.xml new file mode 100644 index 0000000..e435841 --- /dev/null +++ b/src/main/resources/spring.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file