MCSM: Сервер управления серверами minecraft
This commit is contained in:
@@ -1,16 +1,31 @@
|
|||||||
group = 'asys'
|
group = 'asys'
|
||||||
version = '0.3-SNAPSHOT'
|
version = '0.4-SNAPSHOT'
|
||||||
|
|
||||||
apply plugin: 'osgi'
|
apply plugin: 'osgi'
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
included
|
||||||
|
compile.extendsFrom included
|
||||||
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
dependsOn configurations.included
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
name = 'ASys MC server manager'
|
name = 'ASys MC server manager'
|
||||||
instruction 'Bundle-Activator', 'asys.mcsmanager.Activator'
|
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 {
|
dependencies {
|
||||||
compile project(':core')
|
compile project(':core')
|
||||||
compile project(':webinterface')
|
compile project(':webinterface')
|
||||||
|
included group: 'io.netty', name: 'netty-codec', version: nettyVersion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
package asys.mcsmanager;
|
package asys.mcsmanager;
|
||||||
|
|
||||||
|
import asys.mcsmanager.server.Server;
|
||||||
import asys.webinterface.api.Webinterface;
|
import asys.webinterface.api.Webinterface;
|
||||||
import org.osgi.framework.BundleActivator;
|
import org.osgi.framework.BundleActivator;
|
||||||
import org.osgi.framework.BundleContext;
|
import org.osgi.framework.BundleContext;
|
||||||
@@ -18,25 +19,32 @@ public class Activator implements BundleActivator, ServiceListener {
|
|||||||
private ServiceTracker<?, Webinterface> serviceTracker;
|
private ServiceTracker<?, Webinterface> serviceTracker;
|
||||||
private MCSM_WebModule module;
|
private MCSM_WebModule module;
|
||||||
private Webinterface webinterface;
|
private Webinterface webinterface;
|
||||||
|
private Server serverManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(BundleContext context) throws Exception {
|
public void start(BundleContext context) throws Exception {
|
||||||
module = new MCSM_WebModule();
|
module = new MCSM_WebModule();
|
||||||
|
|
||||||
|
logger.debug("Get service: {}", Webinterface.class);
|
||||||
|
serviceTracker = new ServiceTracker<>(context, Webinterface.class, null);
|
||||||
|
|
||||||
logger.debug("Register service listener");
|
logger.debug("Register service listener");
|
||||||
context.addServiceListener(this);
|
context.addServiceListener(this);
|
||||||
|
|
||||||
logger.debug("Get service: {}", Webinterface.class);
|
logger.debug("Start server manager: {}:{}", "127.0.0.1", 8779);
|
||||||
serviceTracker = new ServiceTracker<>(context, Webinterface.class, null);
|
serverManager = new Server();
|
||||||
|
serverManager.start("127.0.0.1", 8779);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop(BundleContext context) throws Exception {
|
public void stop(BundleContext context) throws Exception {
|
||||||
|
serverManager.shutdown();
|
||||||
|
|
||||||
if (webinterface != null) {
|
if (webinterface != null) {
|
||||||
webinterface.removeModule(module.getName());
|
webinterface.removeModule(module.getName());
|
||||||
}
|
}
|
||||||
serviceTracker.close();
|
|
||||||
context.removeServiceListener(this);
|
context.removeServiceListener(this);
|
||||||
|
serviceTracker.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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