MCSM: Сервер управления серверами minecraft
This commit is contained in:
@@ -1,16 +1,31 @@
|
||||
group = 'asys'
|
||||
version = '0.3-SNAPSHOT'
|
||||
version = '0.4-SNAPSHOT'
|
||||
|
||||
apply plugin: 'osgi'
|
||||
|
||||
configurations {
|
||||
included
|
||||
compile.extendsFrom included
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn configurations.included
|
||||
|
||||
manifest {
|
||||
name = 'ASys MC server manager'
|
||||
instruction 'Bundle-Activator', 'asys.mcsmanager.Activator'
|
||||
instruction 'Import-Package', '!io.netty.*', 'javax.security.cert', 'org.slf4j.helpers', '*'
|
||||
}
|
||||
|
||||
from { configurations.included.collect { it.isDirectory() ? it : zipTree(it).matching{exclude{it.path.contains('META-INF')} } } }
|
||||
}
|
||||
|
||||
ext {
|
||||
nettyVersion = '4.1.9.Final'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':webinterface')
|
||||
included group: 'io.netty', name: 'netty-codec', version: nettyVersion
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
package asys.mcsmanager;
|
||||
|
||||
import asys.mcsmanager.server.Server;
|
||||
import asys.webinterface.api.Webinterface;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
@@ -18,25 +19,32 @@ public class Activator implements BundleActivator, ServiceListener {
|
||||
private ServiceTracker<?, Webinterface> serviceTracker;
|
||||
private MCSM_WebModule module;
|
||||
private Webinterface webinterface;
|
||||
private Server serverManager;
|
||||
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
module = new MCSM_WebModule();
|
||||
|
||||
logger.debug("Get service: {}", Webinterface.class);
|
||||
serviceTracker = new ServiceTracker<>(context, Webinterface.class, null);
|
||||
|
||||
logger.debug("Register service listener");
|
||||
context.addServiceListener(this);
|
||||
|
||||
logger.debug("Get service: {}", Webinterface.class);
|
||||
serviceTracker = new ServiceTracker<>(context, Webinterface.class, null);
|
||||
logger.debug("Start server manager: {}:{}", "127.0.0.1", 8779);
|
||||
serverManager = new Server();
|
||||
serverManager.start("127.0.0.1", 8779);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
serverManager.shutdown();
|
||||
|
||||
if (webinterface != null) {
|
||||
webinterface.removeModule(module.getName());
|
||||
}
|
||||
serviceTracker.close();
|
||||
context.removeServiceListener(this);
|
||||
serviceTracker.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class CS_Handshake extends Packet {
|
||||
private String clientId;
|
||||
private String passcode;
|
||||
|
||||
public CS_Handshake() {
|
||||
}
|
||||
|
||||
public CS_Handshake(String clientId, String passcode) {
|
||||
this.clientId = clientId;
|
||||
this.passcode = passcode;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public String getPasscode() {
|
||||
return passcode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSelfData(ByteBuf buffer) {
|
||||
this.clientId = this.readString(buffer);
|
||||
this.passcode = this.readString(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSelfData(ByteBuf buffer) {
|
||||
this.writeString(buffer, clientId);
|
||||
this.writeString(buffer, passcode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class CS_Ping extends Packet {
|
||||
private long time;
|
||||
private int tps;
|
||||
private int countPlayers;
|
||||
|
||||
public CS_Ping() {
|
||||
}
|
||||
|
||||
public CS_Ping(long time, int tps, int countPlayers) {
|
||||
this.time = time;
|
||||
this.tps = tps;
|
||||
this.countPlayers = countPlayers;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public int getTps() {
|
||||
return tps;
|
||||
}
|
||||
|
||||
public int getCountPlayers() {
|
||||
return countPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSelfData(ByteBuf buffer) {
|
||||
this.time = buffer.readLong();
|
||||
this.tps = buffer.readUnsignedByte();
|
||||
this.countPlayers = buffer.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSelfData(ByteBuf buffer) {
|
||||
buffer.writeLong(time);
|
||||
buffer.writeByte(tps);
|
||||
buffer.writeShort(countPlayers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
public interface IPacketHandler {
|
||||
void handle(Packet packet, ChannelHandlerContext context);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public abstract class Packet {
|
||||
public String readString(ByteBuf buffer) {
|
||||
int length = buffer.readUnsignedShort();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(buffer.readChar());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void writeString(ByteBuf buffer, String string) {
|
||||
int length = string.length();
|
||||
buffer.writeShort(length);
|
||||
|
||||
if (length > 0) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer.writeChar(string.charAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void readSelfData(ByteBuf buffer);
|
||||
public abstract void writeSelfData(ByteBuf buffer);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class SC_HandshakeResult extends Packet {
|
||||
private int errorCode;
|
||||
private String message;
|
||||
|
||||
public SC_HandshakeResult() {
|
||||
}
|
||||
|
||||
public SC_HandshakeResult(int errorCode, String message) {
|
||||
this.errorCode = errorCode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSelfData(ByteBuf buffer) {
|
||||
this.errorCode = buffer.readUnsignedByte();
|
||||
this.message = this.readString(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSelfData(ByteBuf buffer) {
|
||||
buffer.writeByte(errorCode);
|
||||
this.writeString(buffer, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets.codec;
|
||||
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static asys.mcsmanager.packets.codec.Params.KNOWN_HANDLERS;
|
||||
import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS;
|
||||
|
||||
public class PacketDecoder extends ReplayingDecoder<Packet> {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext contect, ByteBuf inBuf, List<Object> out) throws Exception {
|
||||
int id = inBuf.readUnsignedByte();
|
||||
Class<? extends Packet> pktClass = contect.channel().attr(KNOWN_PACKETS).get().get(id);
|
||||
if (pktClass == null) return;
|
||||
|
||||
if (contect.channel().attr(KNOWN_HANDLERS).get().containsKey(pktClass)) {
|
||||
Packet packet = pktClass.newInstance();
|
||||
packet.readSelfData(inBuf);
|
||||
out.add(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets.codec;
|
||||
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS;
|
||||
|
||||
public class PacketEncoder extends MessageToByteEncoder<Packet> {
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext context, Packet packet, ByteBuf outBuf) throws Exception {
|
||||
Integer id = context.channel().attr(KNOWN_PACKETS).get().inverse().get(packet.getClass());
|
||||
if (id == null) return; //TODO в логгере хорошо бы информировать о дропе исходящего пакета
|
||||
|
||||
outBuf.writeByte(id);
|
||||
packet.writeSelfData(outBuf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets.codec;
|
||||
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import static asys.mcsmanager.packets.codec.Params.KNOWN_HANDLERS;
|
||||
|
||||
public class PacketHandler extends SimpleChannelInboundHandler<Packet> {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext context, Packet packet) throws Exception {
|
||||
context.channel().attr(KNOWN_HANDLERS).get().get(packet.getClass()).handle(packet, context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.packets.codec;
|
||||
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import asys.mcsmanager.packets.IPacketHandler;
|
||||
import com.google.common.collect.BiMap;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class Params {
|
||||
public static final AttributeKey<BiMap<Integer, Class<? extends Packet>>> KNOWN_PACKETS = AttributeKey.newInstance("KNOWN_PACKETS");
|
||||
public static final AttributeKey<Map<Class<? extends Packet>, IPacketHandler>> KNOWN_HANDLERS = AttributeKey.newInstance("KNOWN_HANDLERS");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.server;
|
||||
|
||||
import asys.mcsmanager.packets.SC_HandshakeResult;
|
||||
|
||||
abstract class HandshakeResult {
|
||||
static final SC_HandshakeResult OK = new SC_HandshakeResult(0, "OK");
|
||||
static final SC_HandshakeResult INVALID_PASSCODE = new SC_HandshakeResult(1, "Invalid passcode");
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.server;
|
||||
|
||||
import asys.mcsmanager.packets.CS_Ping;
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import asys.mcsmanager.packets.codec.PacketDecoder;
|
||||
import asys.mcsmanager.packets.codec.PacketEncoder;
|
||||
import asys.mcsmanager.packets.codec.PacketHandler;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
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;
|
||||
|
||||
public class Server {
|
||||
public static final BiMap<Integer, Class<? extends Packet>> knownPackets = ImmutableBiMap.of(
|
||||
1, CS_Ping.class
|
||||
);
|
||||
private EventLoopGroup bossGroup, workerGroup;
|
||||
|
||||
public void start(String host, int port) {
|
||||
bossGroup = new NioEventLoopGroup(1);
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
|
||||
ServerBootstrap serverBootstrap = createServerBootstrap();
|
||||
try {
|
||||
serverBootstrap.bind(host, port).sync().channel().closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
private ServerBootstrap createServerBootstrap() {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
|
||||
bootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(createChannelInitializer());
|
||||
|
||||
return bootstrap;
|
||||
}
|
||||
|
||||
private ChannelInitializer createChannelInitializer() {
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
socketChannel.pipeline().addLast(
|
||||
new PacketEncoder(),
|
||||
new PacketDecoder(),
|
||||
new PacketHandler(),
|
||||
new ServerPacketHandler()
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* DmitriyMX <dimon550@gmail.com>
|
||||
* 2017-04-26
|
||||
*/
|
||||
package asys.mcsmanager.server;
|
||||
|
||||
import asys.mcsmanager.packets.CS_Handshake;
|
||||
import asys.mcsmanager.packets.IPacketHandler;
|
||||
import asys.mcsmanager.packets.Packet;
|
||||
import asys.mcsmanager.packets.SC_HandshakeResult;
|
||||
import asys.mcsmanager.packets.codec.Params;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler {
|
||||
private static final BiMap<Integer, Class<? extends Packet>> handshakePackets = ImmutableBiMap.of(
|
||||
1, CS_Handshake.class,
|
||||
2, SC_HandshakeResult.class
|
||||
);
|
||||
private static final String PASSCODE = "testpassphrase";
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
context.channel().attr(Params.KNOWN_PACKETS).set(handshakePackets);
|
||||
super.channelActive(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet packet, ChannelHandlerContext context) {
|
||||
if (packet.getClass() == CS_Handshake.class) handleCSHandshake((CS_Handshake) packet, context);
|
||||
}
|
||||
|
||||
private void handleCSHandshake(CS_Handshake packet, ChannelHandlerContext context) {
|
||||
if (!packet.getPasscode().equalsIgnoreCase(PASSCODE)) {
|
||||
try {
|
||||
context.channel().writeAndFlush(HandshakeResult.INVALID_PASSCODE).sync().channel().close();
|
||||
} catch (InterruptedException ignore) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
context.channel().writeAndFlush(HandshakeResult.OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user