Compare commits
5 Commits
g2/develop
...
g2/feature
| Author | SHA1 | Date | |
|---|---|---|---|
|
0f4e8c0643
|
|||
|
cf16e9373f
|
|||
|
fba59bb009
|
|||
|
b1efb13b05
|
|||
|
c000b82374
|
39
build.gradle
39
build.gradle
@@ -15,36 +15,17 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
slf4j_version = '1.7.30'
|
libs_dir = 'libs'
|
||||||
logback_version = '1.2.3'
|
jline_version = '3.14.1'
|
||||||
netty_version = '4.1.22.Final'
|
|
||||||
|
|
||||||
library = [
|
|
||||||
guice: ['com.google.inject:guice:4.1.0'],
|
|
||||||
logger: ["ch.qos.logback:logback-core:$logback_version",
|
|
||||||
"ch.qos.logback:logback-classic:$logback_version"],
|
|
||||||
lombok: ['org.projectlombok:lombok:1.18.2'],
|
|
||||||
netty: ["io.netty:netty-transport:$netty_version",
|
|
||||||
"io.netty:netty-codec:$netty_version",
|
|
||||||
"io.netty:netty-handler:$netty_version" /*for logger handler*/],
|
|
||||||
slf4j: ["org.slf4j:slf4j-api:$slf4j_version",
|
|
||||||
"org.slf4j:jcl-over-slf4j:$slf4j_version"],
|
|
||||||
commons: ['commons-io:commons-io:2.6']
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
/* LOGGER */
|
compile group: 'org.jline', name: 'jline', version: jline_version
|
||||||
implementation library.slf4j
|
compile group: 'org.jline', name: 'jline-terminal-jansi', version: jline_version
|
||||||
implementation library.logger
|
}
|
||||||
|
|
||||||
/* LOMBOK */
|
task copyDeps(type: Copy) {
|
||||||
annotationProcessor library.lombok
|
into project.buildDir.toPath().resolve('libs').resolve(libs_dir)
|
||||||
compileOnly library.lombok
|
from configurations.runtime
|
||||||
|
}
|
||||||
/* COMPONENTS */
|
jar.dependsOn(copyDeps)
|
||||||
implementation library.guice
|
|
||||||
implementation library.netty
|
|
||||||
implementation library.commons
|
|
||||||
implementation project(':protocol')
|
|
||||||
}
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
rootProject.name = projectName
|
rootProject.name = projectName
|
||||||
|
|
||||||
include ':protocol'
|
|
||||||
project(':protocol').projectDir = new File(settingsDir, '../mc-protocol')
|
|
||||||
@@ -1,16 +1,48 @@
|
|||||||
package mc.server;
|
package mc.server;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
import mc.server.shell.CommandService;
|
||||||
import com.google.inject.Injector;
|
import mc.server.shell.CommandServiceImpl;
|
||||||
import mc.server.config.NetworkModule;
|
import mc.server.shell.SystemPropertiesCommand;
|
||||||
import mc.server.network.Server;
|
import org.jline.builtins.CommandRegistry;
|
||||||
|
import org.jline.reader.*;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
final Injector injector = Guice.createInjector(new NetworkModule());
|
System.out.println("mc-server");
|
||||||
|
|
||||||
final Server server = injector.getInstance(Server.class);
|
CommandService commandService = new CommandServiceImpl();
|
||||||
server.start("127.0.0.1", 25565);
|
commandService.register("system-properties", new SystemPropertiesCommand());
|
||||||
|
|
||||||
|
final Completer completer = CommandRegistry.compileCompleters(commandService);
|
||||||
|
|
||||||
|
final LineReader reader = LineReaderBuilder.builder()
|
||||||
|
.completer(completer)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final CommandRegistry.CommandSession session = new CommandRegistry.CommandSession(reader.getTerminal());
|
||||||
|
final PrintWriter terminalWriter = reader.getTerminal().writer();
|
||||||
|
//noinspection InfiniteLoopStatement
|
||||||
|
while (true) {
|
||||||
|
final String line = reader.readLine().trim();
|
||||||
|
|
||||||
|
final ParsedLine parsedLine = reader.getParser().parse(line, 0);
|
||||||
|
final String commandName = parsedLine.word();
|
||||||
|
final String[] argv = parsedLine.words().subList(1, parsedLine.words().size()).toArray(new String[0]);
|
||||||
|
|
||||||
|
if (commandService.hasCommand(commandName)) {
|
||||||
|
commandService.execute(session, commandName, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminalWriter.println("line = " + String.join(", ", parsedLine.words()));
|
||||||
|
}
|
||||||
|
} catch (UserInterruptException ignore) {
|
||||||
|
// ignore
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
package mc.server.config;
|
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
|
||||||
import com.google.inject.Provides;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import com.google.inject.name.Named;
|
|
||||||
import com.google.inject.name.Names;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
|
||||||
import mc.protocol.PacketDirection;
|
|
||||||
import mc.protocol.io.coder.ProtocolDecoder;
|
|
||||||
import mc.protocol.io.coder.ProtocolEncoder;
|
|
||||||
import mc.protocol.io.coder.ProtocolSplitter;
|
|
||||||
import mc.server.network.Server;
|
|
||||||
import mc.server.network.impl.ChannelInitializer;
|
|
||||||
import mc.server.network.impl.NettyServer;
|
|
||||||
import mc.server.network.impl.codec.PacketDecoder;
|
|
||||||
import mc.server.network.impl.codec.PacketEncoder;
|
|
||||||
import mc.server.network.impl.codec.PacketSplitter;
|
|
||||||
import mc.server.network.impl.handler.HandshakeHandler;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class NetworkModule extends AbstractModule {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure() {
|
|
||||||
bind(Server.class).to(NettyServer.class).in(Singleton.class);
|
|
||||||
bind(EventLoopGroup.class).annotatedWith(Names.named("bossGroup")).toInstance(new NioEventLoopGroup(1));
|
|
||||||
bind(EventLoopGroup.class).annotatedWith(Names.named("workerGroup")).toInstance(new NioEventLoopGroup());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
ServerBootstrap serverBootstrap(@Named("bossGroup") EventLoopGroup bossGroup,
|
|
||||||
@Named("workerGroup") EventLoopGroup workerGroup,
|
|
||||||
ChannelInitializer channelChannelInitializer) {
|
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
|
||||||
|
|
||||||
bootstrap.group(bossGroup, workerGroup)
|
|
||||||
.channel(NioServerSocketChannel.class)
|
|
||||||
.childHandler(channelChannelInitializer);
|
|
||||||
|
|
||||||
return bootstrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named("channelHandlerMap")
|
|
||||||
Map<String, ChannelHandler> channelHandlerMap(HandshakeHandler handshakeHandler) {
|
|
||||||
final Map<String, ChannelHandler> map = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
map.put("logger", new LoggingHandler());
|
|
||||||
map.put("packet_splitter", new PacketSplitter(new ProtocolSplitter()));
|
|
||||||
map.put("packet_decoder", new PacketDecoder(new ProtocolDecoder(PacketDirection.SERVER_BOUND)));
|
|
||||||
map.put("packet_encoder", new PacketEncoder(new ProtocolEncoder(PacketDirection.CLIENT_BOUND)));
|
|
||||||
map.put("handshake_handler", handshakeHandler);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package mc.server.network;
|
|
||||||
|
|
||||||
public interface Server {
|
|
||||||
|
|
||||||
void start(String host, int port);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package mc.server.network.impl;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import com.google.inject.name.Named;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.socket.SocketChannel;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ChannelInitializer extends io.netty.channel.ChannelInitializer<SocketChannel> {
|
|
||||||
|
|
||||||
private final Provider<Map<String, ChannelHandler>> channelHandlerMapProvider;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ChannelInitializer(
|
|
||||||
@Named("channelHandlerMap") Provider<Map<String, ChannelHandler>> channelHandlerMapProvider) {
|
|
||||||
this.channelHandlerMapProvider = channelHandlerMapProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(SocketChannel socketChannel) {
|
|
||||||
final ChannelPipeline pipeline = socketChannel.pipeline();
|
|
||||||
channelHandlerMapProvider.get().forEach(pipeline::addLast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package mc.server.network.impl;
|
|
||||||
|
|
||||||
import io.netty.util.AttributeKey;
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import mc.protocol.State;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class NettyConstants {
|
|
||||||
|
|
||||||
public static final AttributeKey<State> ATTR_STATE = AttributeKey.newInstance("ATTR_STATE");
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package mc.server.network.impl;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import mc.server.network.Server;
|
|
||||||
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class NettyServer implements Server {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private Provider<ServerBootstrap> serverBootstrapProvider;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(String host, int port) {
|
|
||||||
try {
|
|
||||||
log.info("Network starting: {}:{}", host, port);
|
|
||||||
serverBootstrapProvider.get()
|
|
||||||
.bind(host, port)
|
|
||||||
.sync().channel().closeFuture().sync();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
if (log.isTraceEnabled()) {
|
|
||||||
log.trace("{}: {}", e.getClass().getSimpleName(), e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package mc.server.network.impl.codec;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import mc.protocol.Packet;
|
|
||||||
import mc.protocol.State;
|
|
||||||
import mc.protocol.io.coder.ProtocolDecoder;
|
|
||||||
import mc.server.network.impl.io.ByteBufNetInputStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static mc.server.network.impl.NettyConstants.ATTR_STATE;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class PacketDecoder extends ByteToMessageDecoder {
|
|
||||||
|
|
||||||
private final ProtocolDecoder protocolDecoder;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
ctx.channel().attr(ATTR_STATE).set(State.HANDSHAKING);
|
|
||||||
super.channelActive(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
ctx.channel().attr(ATTR_STATE).set(null);
|
|
||||||
super.channelInactive(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
|
||||||
final State state = Objects.requireNonNull(ctx.channel().attr(ATTR_STATE).get());
|
|
||||||
|
|
||||||
final Packet packet = protocolDecoder.decode(state, new ByteBufNetInputStream(in));
|
|
||||||
if (packet != null) {
|
|
||||||
out.add(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package mc.server.network.impl.codec;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import mc.protocol.Packet;
|
|
||||||
import mc.protocol.State;
|
|
||||||
import mc.protocol.io.coder.ProtocolEncoder;
|
|
||||||
import mc.server.network.impl.io.ByteBufNetOutputStream;
|
|
||||||
|
|
||||||
import static mc.server.network.impl.NettyConstants.ATTR_STATE;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class PacketEncoder extends MessageToByteEncoder<Packet> {
|
|
||||||
|
|
||||||
private final ProtocolEncoder protocolEncoder;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) {
|
|
||||||
final State state = ctx.channel().attr(ATTR_STATE).get();
|
|
||||||
protocolEncoder.encode(state, packet, new ByteBufNetOutputStream(out));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package mc.server.network.impl.codec;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import mc.protocol.io.NetInputStream;
|
|
||||||
import mc.protocol.io.coder.ProtocolSplitter;
|
|
||||||
import mc.server.network.impl.io.ByteBufNetInputStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class PacketSplitter extends ByteToMessageDecoder {
|
|
||||||
|
|
||||||
private final ProtocolSplitter protocolSplitter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
|
||||||
final NetInputStream netInputStream = protocolSplitter.split(new ByteBufNetInputStream(in));
|
|
||||||
|
|
||||||
if (netInputStream != null) {
|
|
||||||
byte[] buff = new byte[netInputStream.readableBytes()];
|
|
||||||
netInputStream.readBytes(buff);
|
|
||||||
out.add(Unpooled.wrappedBuffer(buff));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package mc.server.network.impl.handler;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
|
||||||
import mc.protocol.Packet;
|
|
||||||
|
|
||||||
public abstract class AbstractPacketHandler<P extends Packet> extends SimpleChannelInboundHandler<P> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, P msg) throws Exception {
|
|
||||||
channelRead1(ctx, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void channelRead1(ChannelHandlerContext ctx, P packet) throws Exception;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package mc.server.network.impl.handler;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import mc.protocol.State;
|
|
||||||
import mc.protocol.handshake.client.HandshakePacket;
|
|
||||||
|
|
||||||
import static mc.server.network.impl.NettyConstants.ATTR_STATE;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
|
||||||
public class HandshakeHandler extends AbstractPacketHandler<HandshakePacket> {
|
|
||||||
|
|
||||||
private final Provider<StatusHandler> statusHandlerProvider;
|
|
||||||
private final Provider<PingHandler> pingHandlerProvider;
|
|
||||||
private final Provider<LoginHandler> loginHandlerProvider;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead1(ChannelHandlerContext ctx, HandshakePacket packet) {
|
|
||||||
log.info("{}", packet);
|
|
||||||
|
|
||||||
ctx.channel().attr(ATTR_STATE).set(packet.getNextState());
|
|
||||||
|
|
||||||
if (packet.getNextState() == State.STATUS) {
|
|
||||||
ctx.channel().pipeline().replace("handshake_handler", "status_handler", statusHandlerProvider.get());
|
|
||||||
ctx.channel().pipeline().addAfter("status_handler", "ping_handler", pingHandlerProvider.get());
|
|
||||||
} else if (packet.getNextState() == State.LOGIN) {
|
|
||||||
ctx.channel().pipeline().replace("handshake_handler", "login_handler", loginHandlerProvider.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package mc.server.network.impl.handler;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import mc.protocol.login.client.LoginStartPacket;
|
|
||||||
import mc.protocol.login.server.DisconnectPacket;
|
|
||||||
import mc.protocol.text.Text;
|
|
||||||
import mc.protocol.text.TextColor;
|
|
||||||
import mc.protocol.text.TextStyle;
|
|
||||||
|
|
||||||
public class LoginHandler extends AbstractPacketHandler<LoginStartPacket> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead1(ChannelHandlerContext ctx, LoginStartPacket packet) {
|
|
||||||
DisconnectPacket disconnectPacket = new DisconnectPacket();
|
|
||||||
disconnectPacket.setReason(Text.builder()
|
|
||||||
.append(Text.of(TextColor.WHITE, "Server is "))
|
|
||||||
.color(TextColor.RED).style(TextStyle.BOLD).append("NOT ")
|
|
||||||
.color(TextColor.WHITE).append("available.")
|
|
||||||
.build());
|
|
||||||
|
|
||||||
ctx.channel().writeAndFlush(disconnectPacket).channel().disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package mc.server.network.impl.handler;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import mc.protocol.status.PingPacket;
|
|
||||||
|
|
||||||
public class PingHandler extends AbstractPacketHandler<PingPacket> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead1(ChannelHandlerContext ctx, PingPacket packet) {
|
|
||||||
ctx.writeAndFlush(packet).channel().disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package mc.server.network.impl.handler;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import mc.protocol.ProtocolConstant;
|
|
||||||
import mc.protocol.dto.ServerInfo;
|
|
||||||
import mc.protocol.status.client.StatusServerRequest;
|
|
||||||
import mc.protocol.status.server.StatusServerResponse;
|
|
||||||
import mc.protocol.text.Text;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class StatusHandler extends AbstractPacketHandler<StatusServerRequest> {
|
|
||||||
|
|
||||||
private static final String FAVICON_HEADER = "data:image/png;base64,";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead1(ChannelHandlerContext ctx, StatusServerRequest packet) throws Exception {
|
|
||||||
log.info("{}", packet);
|
|
||||||
|
|
||||||
final ServerInfo.Version version = new ServerInfo.Version();
|
|
||||||
version.setName(ProtocolConstant.PROTOCOL_VERSION_VALUE);
|
|
||||||
version.setProtocol(ProtocolConstant.PROTOCOL_VERSION);
|
|
||||||
|
|
||||||
final ServerInfo.PlayersInfo playersInfo = new ServerInfo.PlayersInfo();
|
|
||||||
playersInfo.setMax(20);
|
|
||||||
playersInfo.setOnline(0);
|
|
||||||
playersInfo.setSamplePlayers(Collections.emptyList());
|
|
||||||
|
|
||||||
final ServerInfo serverInfo = new ServerInfo();
|
|
||||||
serverInfo.setVersion(version);
|
|
||||||
serverInfo.setDescription(Text.of("MC-SERVER 1.8.8"));
|
|
||||||
serverInfo.setFaviconBase64(getEmbeddedIconBase64());
|
|
||||||
serverInfo.setPlayersInfo(playersInfo);
|
|
||||||
|
|
||||||
StatusServerResponse response = new StatusServerResponse();
|
|
||||||
response.setServerInfoDto(serverInfo);
|
|
||||||
|
|
||||||
ctx.channel().writeAndFlush(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getEmbeddedIconBase64() throws IOException {
|
|
||||||
return FAVICON_HEADER + Base64.getEncoder()
|
|
||||||
.encodeToString(IOUtils.toByteArray(getClass().getResourceAsStream("/icon.png")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package mc.server.network.impl.io;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import mc.protocol.io.NetInputStream;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ByteBufNetInputStream extends NetInputStream {
|
|
||||||
|
|
||||||
private final ByteBuf byteBuf;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markReadIndex() {
|
|
||||||
byteBuf.markReaderIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetReadIndex() {
|
|
||||||
byteBuf.resetReaderIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readableBytes() {
|
|
||||||
return byteBuf.readableBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte readByte() {
|
|
||||||
return byteBuf.readByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readBytes(byte[] buffer, int offset, int lengtn) {
|
|
||||||
return byteBuf.readBytes(buffer, offset, lengtn).readableBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package mc.server.network.impl.io;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import mc.protocol.io.NetOutputStream;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ByteBufNetOutputStream extends NetOutputStream {
|
|
||||||
|
|
||||||
private final ByteBuf byteBuf;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeByte(int value) {
|
|
||||||
byteBuf.writeByte(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBytes(byte[] buffer, int offset, int lengtn) {
|
|
||||||
byteBuf.writeBytes(buffer, offset, lengtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
8
src/main/java/mc/server/shell/Command.java
Normal file
8
src/main/java/mc/server/shell/Command.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package mc.server.shell;
|
||||||
|
|
||||||
|
import org.jline.builtins.Builtins;
|
||||||
|
|
||||||
|
public interface Command {
|
||||||
|
|
||||||
|
void execute(Builtins.CommandInput commandInput);
|
||||||
|
}
|
||||||
8
src/main/java/mc/server/shell/CommandService.java
Normal file
8
src/main/java/mc/server/shell/CommandService.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package mc.server.shell;
|
||||||
|
|
||||||
|
import org.jline.builtins.CommandRegistry;
|
||||||
|
|
||||||
|
public interface CommandService extends CommandRegistry {
|
||||||
|
|
||||||
|
void register(String commandName, Command command);
|
||||||
|
}
|
||||||
57
src/main/java/mc/server/shell/CommandServiceImpl.java
Normal file
57
src/main/java/mc/server/shell/CommandServiceImpl.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package mc.server.shell;
|
||||||
|
|
||||||
|
import org.jline.builtins.Builtins;
|
||||||
|
import org.jline.builtins.Builtins.CommandMethods;
|
||||||
|
import org.jline.builtins.Completers.SystemCompleter;
|
||||||
|
import org.jline.reader.impl.completer.NullCompleter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class CommandServiceImpl implements CommandService {
|
||||||
|
|
||||||
|
private final Map<String, CommandMethods> commands = new HashMap<>();
|
||||||
|
|
||||||
|
public void register(String commandName, Command command) {
|
||||||
|
commands.put(commandName, new CommandMethods(
|
||||||
|
command::execute,
|
||||||
|
cmdName -> Collections.singletonList(NullCompleter.INSTANCE)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object execute(CommandSession session, String command) {
|
||||||
|
return execute(session, command, new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute(CommandSession session, String command, String[] args) {
|
||||||
|
commands.get(command).execute().accept(new Builtins.CommandInput(command, args, session));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> commandNames() {
|
||||||
|
return commands.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> commandAliases() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasCommand(String commandName) {
|
||||||
|
return commands.containsKey(commandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemCompleter compileCompleters() {
|
||||||
|
final SystemCompleter systemCompleter = new SystemCompleter();
|
||||||
|
systemCompleter.addAliases(commandAliases());
|
||||||
|
commands.forEach((commandName, command) -> systemCompleter.add(commandName, NullCompleter.INSTANCE));
|
||||||
|
|
||||||
|
return systemCompleter;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/main/java/mc/server/shell/SystemPropertiesCommand.java
Normal file
28
src/main/java/mc/server/shell/SystemPropertiesCommand.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package mc.server.shell;
|
||||||
|
|
||||||
|
import org.jline.builtins.Builtins.CommandInput;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class SystemPropertiesCommand implements Command {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandInput input) {
|
||||||
|
final PrintWriter writer = input.terminal().writer();
|
||||||
|
Stream<Map.Entry<Object, Object>> stream = System.getProperties().entrySet().stream();
|
||||||
|
|
||||||
|
if (input.args().length > 0) {
|
||||||
|
final List<String> argv = Arrays.asList(input.args());
|
||||||
|
stream = stream.filter(entry -> {
|
||||||
|
//noinspection SuspiciousMethodCalls
|
||||||
|
return argv.contains(entry.getKey());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.forEach(entry -> writer.printf("%s = %s\n", entry.getKey(), entry.getValue().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
Reference in New Issue
Block a user