diff --git a/zond/build.gradle b/zond/build.gradle index 36d2c8c..6748cd3 100644 --- a/zond/build.gradle +++ b/zond/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.1-SNAPSHOT' +version = '0.2-SNAPSHOT' apply plugin: 'application' @@ -10,7 +10,13 @@ configurations { compile.extendsFrom included } +compileJava { + dependsOn ':bridge-protocol:compileJava' +} + jar { + dependsOn ':bridge-protocol:jar' + dependsOn configurations.included manifest { attributes 'Implementation-Title': 'ASys Zond', 'Implementation-Version': version, @@ -20,7 +26,14 @@ jar { from { configurations.included.collect { it.isDirectory() ? it : zipTree(it) } } } +ext { + nettyVersion = '4.0.23.Final' +} + dependencies { + included files(project(':bridge-protocol').sourceSets.main.output.classesDir) included group: 'org.fusesource.jansi', name: 'jansi', version: '1.11' included 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/Config.java b/zond/src/main/java/asys/zond/Config.java new file mode 100644 index 0000000..b3092e1 --- /dev/null +++ b/zond/src/main/java/asys/zond/Config.java @@ -0,0 +1,35 @@ +/* + * DmitriyMX + * 2017-06-08 + */ +package asys.zond; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class Config { + private static Properties properties = new Properties(); + + private Config(){ + } + + public static void load(InputStream inputStream) throws IOException { + properties.load(inputStream); + if (properties.size() == 0) { + throw new IOException("Config empty!"); + } + } + + public static String getString(String key) { + return properties.getProperty(key); + } + + public static int getInt(String key) { + try { + return Integer.parseInt(properties.getProperty(key)); + } catch (NumberFormatException e) { + return 0; + } + } +} diff --git a/zond/src/main/java/asys/zond/Main.java b/zond/src/main/java/asys/zond/Main.java index 9623b35..581f6e2 100644 --- a/zond/src/main/java/asys/zond/Main.java +++ b/zond/src/main/java/asys/zond/Main.java @@ -5,6 +5,7 @@ */ package asys.zond; +import asys.zond.proxy.Client; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; @@ -13,13 +14,22 @@ import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi.Color; import org.fusesource.jansi.AnsiConsole; -import java.io.IOException; +import java.io.*; import java.util.Arrays; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.fusesource.jansi.Ansi.ansi; public class Main { + private static Client client; + private static ScheduledExecutorService ses; + private static ScheduledFuture sesFuture; + private static int tryConnect = 0; + public static void main(String[] args) throws IOException { if (Boolean.getBoolean("ansi.install")) { AnsiConsole.systemInstall(); @@ -36,6 +46,21 @@ public class Main { return; } + loadConfig(); + startReconnect(); + + int resultCode = executeProcess(args); + + stopReconnect(); + ses.shutdown(); + client.disconnect(); + + System.out.print(ansi().reset().newline() + .fg(Color.GREEN).a("Process Finished. Code: ") + .bold().fg(Color.WHITE).a(resultCode).reset().newline()); + } + + private static int executeProcess(String[] args) { String cmdLine = Arrays.stream(args).collect(Collectors.joining(" ")); CommandLine commandLine = CommandLine.parse(cmdLine); DefaultExecutor executor = new DefaultExecutor(); @@ -46,9 +71,56 @@ public class Main { try { resultCode = executor.execute(commandLine); } catch (ExecuteException ignore) { + } catch (IOException e) { + e.printStackTrace(); + resultCode = -1; + } + + return resultCode; + } + + private static void loadConfig() throws IOException { + File zondPropertiesFile = new File("zond.properties"); + if (!zondPropertiesFile.exists()) { + InputStream stream = Main.class.getResourceAsStream("/zond.properties"); + FileOutputStream fos = new FileOutputStream(zondPropertiesFile); + byte[] buff = new byte[65536]; + int len; + while ((len = stream.read(buff)) > 0) { + fos.write(buff, 0, len); + } + fos.flush(); + fos.close(); + } + + FileInputStream fis = new FileInputStream(zondPropertiesFile); + Config.load(fis); + fis.close(); + } + + public static void log(String message){ + System.out.println("[ASys Zond] " + message); + } + + public static void startReconnect() { + client = new Client(); + ses = Executors.newScheduledThreadPool(2); + sesFuture = ses.scheduleAtFixedRate(() -> { + log(String.format("Connect(%d) to ASys...", ++tryConnect)); + client.connect(Config.getString("host"), Config.getInt("port")); + if (client.isConnected()) { + stopReconnect(); + } else { + log(String.format("Connection(%d) fail. Try reconnect...", tryConnect)); + } + }, 0L, 5L, TimeUnit.SECONDS); + } + + private static void stopReconnect() { + if (sesFuture != null) { + sesFuture.cancel(false); + sesFuture = null; + tryConnect = 0; } - System.out.print(ansi().reset().newline() - .fg(Color.GREEN).a("Process Finished. Code: ") - .bold().fg(Color.WHITE).a(resultCode).reset().newline()); } } 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..114b4c9 --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/Client.java @@ -0,0 +1,60 @@ +/* + * DmitriyMX + * 2017-06-08 + */ +package asys.zond.proxy; + +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.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(); + } + } + + public boolean isConnected() { + return (channelFuture != null && channelFuture.isSuccess()); + } + + public void disconnect() { + group.shutdownGracefully(); + } + + private Bootstrap createBootstrap() { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(group) + .channel(NioSocketChannel.class) + .handler(createChannelInitializer()); + + return bootstrap; + } + + private ChannelInitializer 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..e15ada8 --- /dev/null +++ b/zond/src/main/java/asys/zond/proxy/ClientPacketHandler.java @@ -0,0 +1,79 @@ +/* + * DmitriyMX + * 2017-06-08 + */ +package asys.zond.proxy; + +import asys.mcsmanager.packets.*; +import asys.zond.Config; +import asys.zond.Main; +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; +import static asys.zond.Main.log; + +public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler { + private static final BiMap> handshakePackets = ImmutableBiMap.of( + 1, CS_Handshake.class, + 2, SC_HandshakeResult.class + ); + private static Map, IPacketHandler> handshakeHandlers; + + private static final BiMap> knownPackets = ImmutableBiMap.of( + 3, CS_Ping.class, + 4, CS_ConsoleMessage.class + ); + + ClientPacketHandler() { + if (handshakeHandlers == null) { + handshakeHandlers = ImmutableMap.of( + SC_HandshakeResult.class, this + ); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log("channelActive"); + ctx.channel().attr(KNOWN_PACKETS).set(handshakePackets); + ctx.channel().attr(KNOWN_HANDLERS).set(handshakeHandlers); + + CS_Handshake packet = new CS_Handshake( + Config.getString("clientId"), + Config.getString("passcode")); + log("send Handshake packet..."); + ctx.channel().writeAndFlush(packet); + + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log("Lost connection!"); + log("Try reconnect..."); + Main.startReconnect(); + + ctx.channel().attr(KNOWN_PACKETS).remove(); + ctx.channel().attr(KNOWN_HANDLERS).remove(); + super.channelInactive(ctx); + } + + @Override + public void handle(Packet packet, ChannelHandlerContext context) { + log("handle : " + packet.getClass().getSimpleName()); + SC_HandshakeResult pkt = (SC_HandshakeResult) packet; + if (pkt.getErrorCode() != 0) { + log(String.format("Handshake: #%d %s", pkt.getErrorCode(), pkt.getMessage())); + } else { + context.channel().attr(KNOWN_PACKETS).set(knownPackets); + log("Handshake: OK"); + } + } +} diff --git a/zond/src/main/resources/zond.properties b/zond/src/main/resources/zond.properties new file mode 100644 index 0000000..547e091 --- /dev/null +++ b/zond/src/main/resources/zond.properties @@ -0,0 +1,4 @@ +clientId = SpigotServer0 +host = 127.0.0.1 +port = 8779 +passcode = testpassphrase \ No newline at end of file