diff --git a/zond/build.gradle b/zond/build.gradle index 42a51f7..9f41be0 100644 --- a/zond/build.gradle +++ b/zond/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.7.3-SNAPSHOT' +version = '0.7.4-SNAPSHOT' apply plugin: 'application' @@ -18,6 +18,10 @@ def zp(FileTree ft) { } } +compileJava { + dependsOn ':libprotocol:compileJava' +} + jar { dependsOn configurations.included dependsOn configurations.includedEx @@ -31,7 +35,14 @@ jar { from { configurations.includedEx.collect { it.isDirectory() ? it : zp(zipTree(it)) } } } +ext { + nettyVersion = '4.0.23.Final' +} + dependencies { + included files(project(':libprotocol').sourceSets.main.output.classesDir) included group: 'jline', name: 'jline', version: '2.14.3' includedEx group: 'org.apache.commons', name: 'commons-exec', version: '1.3' + included group: 'io.netty', name: 'netty-codec', version: nettyVersion + included group: 'com.google.guava', name: 'guava', version: '21.0' } diff --git a/zond/src/main/java/asys/zond/ZondCommandHandler.java b/zond/src/main/java/asys/zond/ZondCommandHandler.java index cd5be4e..3b08abe 100644 --- a/zond/src/main/java/asys/zond/ZondCommandHandler.java +++ b/zond/src/main/java/asys/zond/ZondCommandHandler.java @@ -4,6 +4,7 @@ */ package asys.zond; +import asys.zond.proxy.Connector; import asys.zond.shell.CommandHandler; import asys.zond.shell.Shell; import org.apache.commons.exec.CommandLine; @@ -34,6 +35,7 @@ public class ZondCommandHandler implements CommandHandler { watchdog.destroyProcess(); threadExec.interrupt(); } + Connector.getInstance().shutdown(); Shell.getInstance().shutdown(); } else if (line.equalsIgnoreCase("start")) { if (watchdog == null) { @@ -59,6 +61,8 @@ public class ZondCommandHandler implements CommandHandler { watchdog.destroyProcess(); threadExec.interrupt(); } + } else if (line.equalsIgnoreCase("connect")) { + Connector.getInstance().startReconnect("127.0.0.1", 8779); } } diff --git a/zond/src/main/java/asys/zond/proxy/Client.java b/zond/src/main/java/asys/zond/proxy/Client.java new file mode 100644 index 0000000..5500af6 --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/Client.java @@ -0,0 +1,72 @@ +/* + * DmitriyMX + * 2017-06-16 + */ +package asys.zond.proxy; + +import asys.mcsmanager.packets.Packet; +import asys.mcsmanager.packets.codec.PacketDecoder; +import asys.mcsmanager.packets.codec.PacketEncoder; +import asys.mcsmanager.packets.codec.PacketHandler; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelFuture; +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.NioSocketChannel; + +public class Client { + private EventLoopGroup group; + private Bootstrap bootstrap; + private ChannelFuture channelFuture; + + public void connect(String host, int port) { + if (group == null || bootstrap == null) { + group = new NioEventLoopGroup(); + bootstrap = createBootstrap(); + } + + channelFuture = bootstrap.connect(host, port); + channelFuture.awaitUninterruptibly(5000); + } + + public void disconnect() { + channelFuture.channel().close(); + group.shutdownGracefully(); + } + + public boolean isConnected() { + return (channelFuture != null && channelFuture.isSuccess()); + } + + public void sendPacket(Packet packet) { + if (isConnected()) { + channelFuture.channel().writeAndFlush(packet); + } + } + + private Bootstrap createBootstrap() { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(group) + .channel(NioSocketChannel.class) + .handler(createChannelInitializer()); + + return bootstrap; + } + + private ChannelHandler createChannelInitializer() { + return new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + socketChannel.pipeline().addLast( + new PacketEncoder(), + new PacketDecoder(), + new PacketHandler(), + new ClientPacketHandler() + ); + } + }; + } +} diff --git a/zond/src/main/java/asys/zond/proxy/ClientPacketHandler.java b/zond/src/main/java/asys/zond/proxy/ClientPacketHandler.java new file mode 100644 index 0000000..1de976e --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/ClientPacketHandler.java @@ -0,0 +1,70 @@ +/* + * DmitriyMX + * 2017-06-16 + */ +package asys.zond.proxy; + +import asys.mcsmanager.packets.*; +import asys.zond.shell.Shell; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableMap; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.util.Map; + +import static asys.mcsmanager.packets.codec.Params.KNOWN_HANDLERS; +import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS; + +public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler { + private final BiMap> handshakePackets; + private final Map, IPacketHandler> handshakeHandlers; + private final BiMap> knownPackets; + + ClientPacketHandler() { + handshakePackets = ImmutableBiMap.of( + 1, CS_Handshake.class, + 2, SC_HandshakeResult.class + ); + handshakeHandlers = ImmutableMap.of( + SC_HandshakeResult.class, this + ); + + knownPackets = ImmutableBiMap.of( + 3, CS_Ping.class, + 4, CS_ConsoleMessage.class + ); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(KNOWN_PACKETS).set(handshakePackets); + ctx.channel().attr(KNOWN_HANDLERS).set(handshakeHandlers); + + ctx.channel().writeAndFlush(new CS_Handshake("00","11")); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(KNOWN_PACKETS).remove(); + ctx.channel().attr(KNOWN_HANDLERS).remove(); + } + + @Override + public void handle(Packet packet, ChannelHandlerContext context) { + if (packet instanceof SC_HandshakeResult) { + handleHandshakeResult((SC_HandshakeResult) packet, context); + } + } + + private void handleHandshakeResult(SC_HandshakeResult packet, ChannelHandlerContext context) { + if (packet.getErrorCode() != 0) { + Shell.getInstance().getOutput() + .println(String.format("Handshake: #%d %s", packet.getErrorCode(), packet.getMessage())); + } else { + context.channel().attr(KNOWN_PACKETS).set(knownPackets); + Shell.getInstance().getOutput().println("Handshake: OK"); + } + } +} diff --git a/zond/src/main/java/asys/zond/proxy/Connector.java b/zond/src/main/java/asys/zond/proxy/Connector.java new file mode 100644 index 0000000..f7ced12 --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/Connector.java @@ -0,0 +1,58 @@ +/* + * DmitriyMX + * 2017-06-16 + */ +package asys.zond.proxy; + +import asys.mcsmanager.packets.Packet; +import asys.zond.shell.Shell; + +public class Connector { + private static Connector instance; + private Client client; + private TaskTicker connectTicker; + private int tryConnect = 0; + + public static Connector getInstance() { + if (instance == null) instance = new Connector(); + return instance; + } + + private Connector() { + } + + public void startReconnect(final String host, final int port) { + if (connectTicker != null && connectTicker.isActive()) return; + client = new Client(); + connectTicker = new TaskTicker().setStepTimeMs(5000L); + connectTicker.setTask(() -> { + Shell.getInstance().getOutput() + .println(String.format("Connect(%d) to ASys...", ++tryConnect)); + client.connect(host, port); + if (client.isConnected()) { + stopReconnect(); + } else { + Shell.getInstance().getOutput() + .println(String.format("Connection(%d) fail. Try reconnect...", tryConnect)); + } + }).start(); + } + + public void stopReconnect() { + if (connectTicker != null) { + connectTicker.stop(); + tryConnect = 0; + } + } + + public void shutdown() { + stopReconnect(); + client.disconnect(); + } + + public void sendPacket(Packet packet) { + if (client != null) { + client.sendPacket(packet); + } + } +} diff --git a/zond/src/main/java/asys/zond/proxy/TaskTicker.java b/zond/src/main/java/asys/zond/proxy/TaskTicker.java new file mode 100644 index 0000000..52313fb --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/TaskTicker.java @@ -0,0 +1,52 @@ +/* + * DmitriyMX + * 2017-05-18 + */ +package asys.zond.proxy; + +public class TaskTicker implements Runnable { + private Runnable task; + private long stepTimeMs = 1000L; + private Thread thread; + private boolean loop = false; + + TaskTicker setTask(Runnable task) { + this.task = task; + return this; + } + + TaskTicker setStepTimeMs(long stepTimeMs) { + this.stepTimeMs = stepTimeMs; + return this; + } + + void start() { + thread = new Thread(this, "TaskTicker"); + loop = true; + thread.start(); + } + + void stop() { + loop = false; + if (thread != null) { + thread.interrupt(); + } + } + + boolean isActive() { + return loop; + } + + @Override + public void run() { + while (loop || !Thread.currentThread().isInterrupted()) { + task.run(); + + try { + Thread.sleep(stepTimeMs); + } catch (InterruptedException e) { + break; + } + } + } +}