Merge branch 'dev-zond' into dev-webconsole
This commit is contained in:
11
bridge/README.MD
Normal file
11
bridge/README.MD
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Bridge
|
||||||
|
|
||||||
|
Данный плагин служит "мостом" между сервером **Spigot** и **ASys**
|
||||||
|
|
||||||
|
## Настройка
|
||||||
|
|
||||||
|
Настройки хранятся в файле `config.yml`.
|
||||||
|
|
||||||
|
`port` - порт для подключения к **Zond**.
|
||||||
|
|
||||||
|
`second` - раз в сколько секунд отправлять пинг-сигнал
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
group = 'asys'
|
group = 'asys'
|
||||||
version = '0.7.1-SNAPSHOT'
|
version = '0.8.4'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://hub.spigotmc.org/nexus/content/groups/public/' }
|
maven { url 'https://hub.spigotmc.org/nexus/content/groups/public/' }
|
||||||
@@ -22,6 +22,7 @@ compileJava {
|
|||||||
|
|
||||||
jar {
|
jar {
|
||||||
dependsOn configurations.include
|
dependsOn configurations.include
|
||||||
|
baseName = project.group + '.' + project.name
|
||||||
from { configurations.include.collect { it.isDirectory() ? it : zipTree(it) } }
|
from { configurations.include.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,21 @@ import asys.bridge.client.AbstractBridge;
|
|||||||
import asys.bridge.client.IBridge;
|
import asys.bridge.client.IBridge;
|
||||||
import asys.bridge.client.IConfig;
|
import asys.bridge.client.IConfig;
|
||||||
import asys.bridge.client.ILogger;
|
import asys.bridge.client.ILogger;
|
||||||
import io.netty.channel.Channel;
|
import asys.mcsmanager.packets.CS_Ping;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.core.Logger;
|
|
||||||
|
|
||||||
public class BridgeBukkitImpl extends AbstractBridge implements IBridge {
|
public class BridgeBukkitImpl extends AbstractBridge implements IBridge {
|
||||||
private BridgePlugin plugin;
|
private BridgePlugin plugin;
|
||||||
private LoggerBukkitImpl logger;
|
private LoggerBukkitImpl logger;
|
||||||
private ConfigBukkitImpl configBukkit;
|
private ConfigBukkitImpl configBukkit;
|
||||||
private BridgeLoggerAppender loggerAppender;
|
private int scheduleId;
|
||||||
|
private int second;
|
||||||
|
|
||||||
public BridgeBukkitImpl(BridgePlugin plugin) {
|
BridgeBukkitImpl(BridgePlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.logger = new LoggerBukkitImpl(plugin.getLogger());
|
this.logger = new LoggerBukkitImpl(plugin.getLogger());
|
||||||
this.configBukkit = new ConfigBukkitImpl(plugin.getConfig());
|
this.configBukkit = new ConfigBukkitImpl(plugin.getConfig());
|
||||||
|
|
||||||
((Logger) LogManager.getRootLogger()).addAppender(loggerAppender = new BridgeLoggerAppender());
|
this.second = this.configBukkit.getInt("second");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,7 +49,17 @@ public class BridgeBukkitImpl extends AbstractBridge implements IBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChannelFromConsoleMessages(Channel channel) {
|
public void startPing() {
|
||||||
this.loggerAppender.setChannel(channel);
|
scheduleId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin,() -> {
|
||||||
|
this.client.sendPacket(new CS_Ping(
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
20.0D, //FIXME fake tps
|
||||||
|
getCountOnlinePlayers()));
|
||||||
|
}, 0, 20L * second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopPing() {
|
||||||
|
plugin.getServer().getScheduler().cancelTask(scheduleId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* DmitriyMX <dimon550@gmail.com>
|
|
||||||
* 2017-05-17
|
|
||||||
*/
|
|
||||||
package asys.bridge.bukkit;
|
|
||||||
|
|
||||||
import asys.mcsmanager.packets.CS_ConsoleMessage;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import org.apache.logging.log4j.core.LogEvent;
|
|
||||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
|
||||||
|
|
||||||
public class BridgeLoggerAppender extends AbstractAppender {
|
|
||||||
private Channel channel;
|
|
||||||
|
|
||||||
BridgeLoggerAppender() {
|
|
||||||
super("ASysBridge", null, null);
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void append(LogEvent event) {
|
|
||||||
if (channel == null) return;
|
|
||||||
|
|
||||||
long timeMillis = event.getMillis();
|
|
||||||
int intLevel = event.getLevel().intLevel();
|
|
||||||
String loggerName = event.getLoggerName();
|
|
||||||
String message = event.getMessage().getFormattedMessage();
|
|
||||||
|
|
||||||
CS_ConsoleMessage messagePkg = new CS_ConsoleMessage(
|
|
||||||
timeMillis,
|
|
||||||
intLevel,
|
|
||||||
loggerName,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
|
|
||||||
channel.writeAndFlush(messagePkg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChannel(Channel channel) {
|
|
||||||
this.channel = channel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,8 +18,14 @@ public class BridgePlugin extends JavaPlugin {
|
|||||||
this.bridgeBukkit.startReconnect();
|
this.bridgeBukkit.startReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
this.bridgeBukkit.startPing();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
|
this.bridgeBukkit.sendCorrectShutdownPacket();
|
||||||
this.bridgeBukkit.setNeedReconnect(false);
|
this.bridgeBukkit.setNeedReconnect(false);
|
||||||
this.bridgeBukkit.stopPing();
|
this.bridgeBukkit.stopPing();
|
||||||
this.bridgeBukkit.stopReconnect();
|
this.bridgeBukkit.stopReconnect();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.bukkit.configuration.file.FileConfiguration;
|
|||||||
public class ConfigBukkitImpl implements IConfig {
|
public class ConfigBukkitImpl implements IConfig {
|
||||||
private FileConfiguration fileConfiguration;
|
private FileConfiguration fileConfiguration;
|
||||||
|
|
||||||
public ConfigBukkitImpl(FileConfiguration fileConfiguration) {
|
ConfigBukkitImpl(FileConfiguration fileConfiguration) {
|
||||||
this.fileConfiguration = fileConfiguration;
|
this.fileConfiguration = fileConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
package asys.bridge.client;
|
package asys.bridge.client;
|
||||||
|
|
||||||
import asys.bridge.bukkit.TaskTicker;
|
import asys.bridge.bukkit.TaskTicker;
|
||||||
|
import asys.mcsmanager.packets.CS_CorrectShutdown;
|
||||||
import asys.mcsmanager.packets.CS_Ping;
|
import asys.mcsmanager.packets.CS_Ping;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
|
|
||||||
public abstract class AbstractBridge implements IBridge {
|
public abstract class AbstractBridge implements IBridge {
|
||||||
private Client client;
|
protected Client client;
|
||||||
private TaskTicker connectTicker, pingTicker;
|
private TaskTicker connectTicker, pingTicker;
|
||||||
private int tryConnect = 0;
|
private int tryConnect = 0;
|
||||||
private boolean needReconnect = true;
|
private boolean needReconnect = true;
|
||||||
@@ -19,8 +19,8 @@ public abstract class AbstractBridge implements IBridge {
|
|||||||
client = new Client(this);
|
client = new Client(this);
|
||||||
connectTicker = new TaskTicker().setStepTimeMs(5000L);
|
connectTicker = new TaskTicker().setStepTimeMs(5000L);
|
||||||
connectTicker.setTask(() -> {
|
connectTicker.setTask(() -> {
|
||||||
getLogger().info(String.format("Connect(%d) to ASys...", ++tryConnect));
|
getLogger().info(String.format("Connect(%d) to Zond...", ++tryConnect));
|
||||||
client.connect(getConfig().getString("host"), getConfig().getInt("port"));
|
client.connect(getConfig().getInt("port"));
|
||||||
if (client.isConnected()) {
|
if (client.isConnected()) {
|
||||||
stopReconnect();
|
stopReconnect();
|
||||||
} else {
|
} else {
|
||||||
@@ -39,6 +39,13 @@ public abstract class AbstractBridge implements IBridge {
|
|||||||
this.needReconnect = value;
|
this.needReconnect = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendCorrectShutdownPacket() {
|
||||||
|
if (client != null && client.isConnected()) {
|
||||||
|
client.sendPacket(new CS_CorrectShutdown());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopReconnect() {
|
public void stopReconnect() {
|
||||||
if (connectTicker != null) {
|
if (connectTicker != null) {
|
||||||
@@ -48,24 +55,13 @@ public abstract class AbstractBridge implements IBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startPing(Channel channel) {
|
public void startPing() {
|
||||||
pingTicker = new TaskTicker().setStepTimeMs(5000L);
|
pingTicker = new TaskTicker().setStepTimeMs(5000L);
|
||||||
pingTicker.setTask(() -> {
|
pingTicker.setTask(() -> {
|
||||||
channel.write(new CS_Ping(
|
client.sendPacket(new CS_Ping(
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
20.0D, //FIXME fake tps
|
20.0D, //FIXME fake tps
|
||||||
getCountOnlinePlayers()
|
getCountOnlinePlayers()));
|
||||||
));
|
|
||||||
if (channel.isWritable()) {
|
|
||||||
channel.flush();
|
|
||||||
} else {
|
|
||||||
getLogger().warn("Lost connection!");
|
|
||||||
channel.close();
|
|
||||||
stopPing();
|
|
||||||
|
|
||||||
getLogger().warn("Try reconnect...");
|
|
||||||
startReconnect();
|
|
||||||
}
|
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
package asys.bridge.client;
|
package asys.bridge.client;
|
||||||
|
|
||||||
|
import asys.mcsmanager.packets.Packet;
|
||||||
import asys.mcsmanager.packets.codec.PacketDecoder;
|
import asys.mcsmanager.packets.codec.PacketDecoder;
|
||||||
import asys.mcsmanager.packets.codec.PacketEncoder;
|
import asys.mcsmanager.packets.codec.PacketEncoder;
|
||||||
import asys.mcsmanager.packets.codec.PacketHandler;
|
import asys.mcsmanager.packets.codec.PacketHandler;
|
||||||
@@ -21,25 +22,25 @@ public class Client {
|
|||||||
private ChannelFuture channelFuture;
|
private ChannelFuture channelFuture;
|
||||||
private IBridge bridge;
|
private IBridge bridge;
|
||||||
|
|
||||||
public Client(IBridge bridge) {
|
Client(IBridge bridge) {
|
||||||
this.bridge = bridge;
|
this.bridge = bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String host, int port) {
|
void connect(int port) {
|
||||||
if (group == null || bootstrap == null) {
|
if (group == null || bootstrap == null) {
|
||||||
group = new NioEventLoopGroup();
|
group = new NioEventLoopGroup();
|
||||||
bootstrap = createBootstrap();
|
bootstrap = createBootstrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
channelFuture = bootstrap.connect(host, port);
|
channelFuture = bootstrap.connect("127.0.0.1", port);
|
||||||
channelFuture.awaitUninterruptibly(5000);
|
channelFuture.awaitUninterruptibly(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
boolean isConnected() {
|
||||||
return (channelFuture != null && channelFuture.isSuccess());
|
return (channelFuture != null && channelFuture.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect() {
|
void disconnect() {
|
||||||
group.shutdownGracefully();
|
group.shutdownGracefully();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,4 +66,10 @@ public class Client {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendPacket(Packet packet) {
|
||||||
|
if (isConnected()) {
|
||||||
|
channelFuture.channel().writeAndFlush(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,27 +17,20 @@ import static asys.mcsmanager.packets.codec.Params.KNOWN_HANDLERS;
|
|||||||
import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS;
|
import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS;
|
||||||
|
|
||||||
public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler {
|
public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler {
|
||||||
private static final BiMap<Integer, Class<? extends Packet>> handshakePackets = ImmutableBiMap.of(
|
private static Map<Class<? extends Packet>, IPacketHandler> knownHandlers;
|
||||||
1, CS_Handshake.class,
|
|
||||||
2, SC_HandshakeResult.class
|
|
||||||
);
|
|
||||||
private static Map<Class<? extends Packet>, IPacketHandler> handshakeHandlers;
|
|
||||||
|
|
||||||
private static final BiMap<Integer, Class<? extends Packet>> knownPackets = ImmutableBiMap.of(
|
private static final BiMap<Integer, Class<? extends Packet>> knownPackets = ImmutableBiMap.of(
|
||||||
3, CS_Ping.class,
|
3, CS_Ping.class,
|
||||||
4, CS_ConsoleMessage.class,
|
|
||||||
5, SC_Command.class,
|
5, SC_Command.class,
|
||||||
6, SC_ToggleSendMessages.class
|
6, CS_CorrectShutdown.class
|
||||||
);
|
);
|
||||||
private IBridge bridge;
|
private IBridge bridge;
|
||||||
|
|
||||||
ClientPacketHandler(IBridge bridge) {
|
ClientPacketHandler(IBridge bridge) {
|
||||||
this.bridge = bridge;
|
this.bridge = bridge;
|
||||||
if (handshakeHandlers == null) {
|
if (knownHandlers == null) {
|
||||||
handshakeHandlers = ImmutableMap.of(
|
knownHandlers = ImmutableMap.of(
|
||||||
SC_HandshakeResult.class, this,
|
SC_Command.class, this
|
||||||
SC_Command.class, this,
|
|
||||||
SC_ToggleSendMessages.class, this
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,14 +38,8 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements
|
|||||||
@Override
|
@Override
|
||||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||||
bridge.getLogger().info("channelActive");
|
bridge.getLogger().info("channelActive");
|
||||||
context.channel().attr(KNOWN_PACKETS).set(handshakePackets);
|
context.channel().attr(KNOWN_PACKETS).set(knownPackets);
|
||||||
context.channel().attr(KNOWN_HANDLERS).set(handshakeHandlers);
|
context.channel().attr(KNOWN_HANDLERS).set(knownHandlers);
|
||||||
|
|
||||||
CS_Handshake packet = new CS_Handshake(
|
|
||||||
bridge.getConfig().getString("clientId"),
|
|
||||||
bridge.getConfig().getString("passcode"));
|
|
||||||
bridge.getLogger().info("send Handshake packet...");
|
|
||||||
context.channel().writeAndFlush(packet);
|
|
||||||
super.channelActive(context);
|
super.channelActive(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,24 +58,8 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements
|
|||||||
@Override
|
@Override
|
||||||
public void handle(Packet packet, ChannelHandlerContext context) {
|
public void handle(Packet packet, ChannelHandlerContext context) {
|
||||||
bridge.getLogger().info("handle : " + packet.getClass().getSimpleName());
|
bridge.getLogger().info("handle : " + packet.getClass().getSimpleName());
|
||||||
if (packet instanceof SC_HandshakeResult) {
|
if (packet instanceof SC_Command) {
|
||||||
handleHandshakeResult((SC_HandshakeResult) packet, context);
|
|
||||||
} else if (packet instanceof SC_Command) {
|
|
||||||
handleCommand((SC_Command) packet);
|
handleCommand((SC_Command) packet);
|
||||||
} else if (packet instanceof SC_ToggleSendMessages) {
|
|
||||||
handleToggleSendMessages((SC_ToggleSendMessages) packet, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleHandshakeResult(SC_HandshakeResult packet, ChannelHandlerContext context) {
|
|
||||||
if (packet.getErrorCode() != 0) {
|
|
||||||
bridge.getLogger().error(
|
|
||||||
String.format("Handshake: #%d %s", packet.getErrorCode(), packet.getMessage()));
|
|
||||||
bridge.setNeedReconnect(false);
|
|
||||||
} else {
|
|
||||||
context.channel().attr(KNOWN_PACKETS).set(knownPackets);
|
|
||||||
bridge.getLogger().info("Handshake: OK");
|
|
||||||
bridge.startPing(context.channel());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +67,4 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements
|
|||||||
bridge.getLogger().info("Command: " + packet.getCommand());
|
bridge.getLogger().info("Command: " + packet.getCommand());
|
||||||
bridge.dispatchCommand(packet.getCommand());
|
bridge.dispatchCommand(packet.getCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleToggleSendMessages(SC_ToggleSendMessages packet, ChannelHandlerContext context) {
|
|
||||||
bridge.setChannelFromConsoleMessages(packet.isNeedSend() ? context.channel() : null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
package asys.bridge.client;
|
package asys.bridge.client;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
|
|
||||||
public interface IBridge {
|
public interface IBridge {
|
||||||
ILogger getLogger();
|
ILogger getLogger();
|
||||||
IConfig getConfig();
|
IConfig getConfig();
|
||||||
int getCountOnlinePlayers();
|
int getCountOnlinePlayers();
|
||||||
void dispatchCommand(String command);
|
void dispatchCommand(String command);
|
||||||
void setChannelFromConsoleMessages(Channel channel);
|
|
||||||
void startReconnect();
|
void startReconnect();
|
||||||
boolean isNeedReconnect();
|
boolean isNeedReconnect();
|
||||||
void setNeedReconnect(boolean value);
|
void setNeedReconnect(boolean value);
|
||||||
|
void sendCorrectShutdownPacket();
|
||||||
void stopReconnect();
|
void stopReconnect();
|
||||||
void startPing(Channel channel);
|
void startPing();
|
||||||
void stopPing();
|
void stopPing();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
clientId: SpigotServer0
|
port: 8710
|
||||||
host: 127.0.0.1
|
second: 5
|
||||||
port: 8779
|
|
||||||
passcode: testpassphrase
|
|
||||||
@@ -13,7 +13,7 @@ subprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
if (it.name != 'bridge' || !it.name.startsWith('lib')) {
|
if (it.name != 'bridge' && it.name != 'zond' && !it.name.startsWith('lib')) {
|
||||||
ext {
|
ext {
|
||||||
slf4jVersion = '1.7.21'
|
slf4jVersion = '1.7.21'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
group = 'asys'
|
group = 'asys'
|
||||||
version = '0.5-SNAPSHOT'
|
version = '0.6-SNAPSHOT'
|
||||||
|
|
||||||
task jar(type: Jar, overwrite: true) {
|
task jar(type: Jar, overwrite: true) {
|
||||||
// не собирать jar
|
// не собирать jar
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-07-22
|
||||||
|
*/
|
||||||
|
package asys.mcsmanager.packets;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public class CS_CorrectShutdown extends Packet {
|
||||||
|
@Override
|
||||||
|
public void readSelfData(ByteBuf buffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeSelfData(ByteBuf buffer) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
group = 'asys'
|
group = 'asys'
|
||||||
version = '0.10.3-SNAPSHOT'
|
version = '0.10.6-SNAPSHOT'
|
||||||
|
|
||||||
apply plugin: 'osgi'
|
apply plugin: 'osgi'
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,10 @@ var WebConsole = React.createClass({
|
|||||||
ce('div', {id: 'webconsole'},
|
ce('div', {id: 'webconsole'},
|
||||||
ce(ScrollingContent, {className: 'output'},
|
ce(ScrollingContent, {className: 'output'},
|
||||||
this.state.lines.map(function(line){
|
this.state.lines.map(function(line){
|
||||||
return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}});
|
var clazz = "";
|
||||||
|
if (line.indexOf('ERROR') !== -1) { clazz = "error"; }
|
||||||
|
else if (line.indexOf('WARN') !== -1) { clazz = "warn"; }
|
||||||
|
return ce('p', {className: clazz, dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}});
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
ce('input', {ref: 'input', 'onKeyPress': this.handleKeyInput})
|
ce('input', {ref: 'input', 'onKeyPress': this.handleKeyInput})
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
color: #eee;
|
color: #eee;
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
padding: 8px;
|
padding: 0 8px 0 8px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -13,16 +13,48 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#webconsole .output .wrapper::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 5%;
|
||||||
|
background: linear-gradient(#1e1e1e 0%, rgba(30,30,30,0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#webconsole .output .wrapper::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 5%;
|
||||||
|
background: linear-gradient(rgba(30,30,30,0) 0%, #1e1e1e 100%);
|
||||||
|
}
|
||||||
|
|
||||||
#webconsole .output .wrapper .content {
|
#webconsole .output .wrapper .content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
right: -18px;
|
right: -18px;
|
||||||
margin-left: -18px;
|
margin-left: -18px;
|
||||||
|
padding: 8px 0 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#webconsole .output .wrapper .content p {
|
#webconsole .output .wrapper .content p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
#webconsole .output .wrapper .content p.error {
|
||||||
|
background-color: rgba(255,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#webconsole .output .wrapper .content p.warn {
|
||||||
|
background-color: rgba(255,200,0,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#webconsole .output .scroll {
|
#webconsole .output .scroll {
|
||||||
@@ -34,6 +66,7 @@
|
|||||||
cursor: -webkit-grab;
|
cursor: -webkit-grab;
|
||||||
cursor: -moz-grab;
|
cursor: -moz-grab;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-grabbed,
|
.scroll-grabbed,
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ include 'webinterface'
|
|||||||
include 'mcserver-manager'
|
include 'mcserver-manager'
|
||||||
include 'libprotocol'
|
include 'libprotocol'
|
||||||
include 'bridge'
|
include 'bridge'
|
||||||
|
include 'zond'
|
||||||
|
|||||||
54
zond/README.MD
Normal file
54
zond/README.MD
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Zond
|
||||||
|
|
||||||
|
Проксирующий запускатор, дающий больший контроль над процессом игрового сервера.
|
||||||
|
|
||||||
|
## Настройка
|
||||||
|
|
||||||
|
Настройки храняться в файле `zond.properties`.
|
||||||
|
Если файл не будет найден, **Zond** создаст файл настроек по-умолчанию
|
||||||
|
|
||||||
|
`asys.serverId` - идентификатор запускаемого сервера. Одновременно является именем и уникальным ID
|
||||||
|
|
||||||
|
`asys.host` - IP/Hostname для подключения к **ASys**
|
||||||
|
|
||||||
|
`asys.port` - порт для подключения к **ASys**
|
||||||
|
|
||||||
|
`asys.passcode` - секретное слово для предотвращения несанкционированного подключения
|
||||||
|
|
||||||
|
`bridge.port` - порт для подключения **Bridge**
|
||||||
|
|
||||||
|
`bridge.second` - раз в сколько секунд **Bridge** будет присылать пинги
|
||||||
|
|
||||||
|
`pingmonitor.delay` - задержка старта пинг-монитора
|
||||||
|
|
||||||
|
`pingmonitor.maxlost` - сколько максимум пинг-сигналов позволено пропустить. По достижению этого числа, игровой сервер считается зависшим
|
||||||
|
|
||||||
|
`cmd.killer` - комманда для "убийства" процесса сервера
|
||||||
|
|
||||||
|
`cmd.start` - комманда для запуска провесса сервера
|
||||||
|
|
||||||
|
`cmd.prestart` - комманда выполняющаяся перед запуском сервера
|
||||||
|
|
||||||
|
`cmd.errorstart` - комманда выполняющаяся в случае проблем с запуском сервера
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```
|
||||||
|
java -jar asys.zond.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## Комманды
|
||||||
|
|
||||||
|
`:reload` - перезагрузить настройки
|
||||||
|
|
||||||
|
`:ticker` - вкл/выкл тикера. Нужен для корректировки задержки старта пинг-монитора
|
||||||
|
|
||||||
|
`:start` - запуск процесса сервера
|
||||||
|
|
||||||
|
`:connect` - подключиться к **ASys**
|
||||||
|
|
||||||
|
`:disconnect` - отключиться от **ASys**
|
||||||
|
|
||||||
|
`:kill` - принудительно завершить процесс сервера
|
||||||
|
|
||||||
|
`:exit` - завершить работу **Zond**.
|
||||||
49
zond/build.gradle
Normal file
49
zond/build.gradle
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
group = 'asys'
|
||||||
|
version = '0.7.17'
|
||||||
|
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName = "asys.zond.Main"
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
included
|
||||||
|
includedEx
|
||||||
|
compile.extendsFrom included
|
||||||
|
compile.extendsFrom includedEx
|
||||||
|
}
|
||||||
|
|
||||||
|
def zp(FileTree ft) {
|
||||||
|
return ft.matching {
|
||||||
|
exclude 'org/apache/commons/exec/StreamPumper.class'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
dependsOn ':libprotocol:compileJava'
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
dependsOn configurations.included
|
||||||
|
dependsOn configurations.includedEx
|
||||||
|
manifest {
|
||||||
|
attributes 'Implementation-Title': 'ASys Zond',
|
||||||
|
'Implementation-Version': version,
|
||||||
|
'Main-Class': mainClassName
|
||||||
|
}
|
||||||
|
baseName = project.group + '.' + project.name
|
||||||
|
from { configurations.included.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||||
|
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'
|
||||||
|
included group: 'com.sun.jna', name: 'jna', version: '3.0.9'
|
||||||
|
}
|
||||||
66
zond/src/main/java/asys/zond/Config.java
Normal file
66
zond/src/main/java/asys/zond/Config.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-08
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
private static final Config instance = new Config();
|
||||||
|
private Properties properties = new Properties();
|
||||||
|
|
||||||
|
public static Config getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config(){
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load(InputStream inputStream) throws IOException {
|
||||||
|
properties.load(inputStream);
|
||||||
|
if (properties.size() == 0) {
|
||||||
|
throw new IOException("Config empty!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() throws IOException {
|
||||||
|
File zondPropertiesFile = new File("zond.properties");
|
||||||
|
if (!zondPropertiesFile.exists()) {
|
||||||
|
InputStream stream = Config.class.getResourceAsStream("/zond.properties");
|
||||||
|
FileOutputStream fos = new FileOutputStream(zondPropertiesFile);
|
||||||
|
byte[] buff = new byte[128];
|
||||||
|
int len;
|
||||||
|
while ((len = stream.read(buff)) > 0) {
|
||||||
|
fos.write(buff, 0, len);
|
||||||
|
}
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(zondPropertiesFile);
|
||||||
|
load(fis);
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key) {
|
||||||
|
return properties.getProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(String key) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(properties.getProperty(key));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong(String key) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(properties.getProperty(key));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
zond/src/main/java/asys/zond/Main.java
Normal file
72
zond/src/main/java/asys/zond/Main.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-15
|
||||||
|
* Idea by Daniil on 2017-06-07
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
import asys.zond.shell.Shell;
|
||||||
|
import org.apache.commons.exec.CommandLine;
|
||||||
|
import org.apache.commons.exec.DefaultExecutor;
|
||||||
|
import org.apache.commons.exec.Executor;
|
||||||
|
import org.apache.commons.exec.PumpStreamHandler;
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
import org.fusesource.jansi.AnsiConsole;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
private Executor executor;
|
||||||
|
private CommandLine commandLine;
|
||||||
|
private PipeInputStream proxyStdIn;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
new Main().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String printLogo() {
|
||||||
|
return Ansi.ansi().bold().fg(Ansi.Color.WHITE).a("ASys")
|
||||||
|
.boldOff().a(":// ")
|
||||||
|
.fg(Ansi.Color.RED).a("Zond")
|
||||||
|
.reset().newline().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
if (Boolean.getBoolean("ansi.install"))
|
||||||
|
AnsiConsole.systemInstall();
|
||||||
|
System.out.println(printLogo());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Config.getInstance().load();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxyStdIn = new PipeInputStream();
|
||||||
|
ZondCommandHandler commandHandler = new ZondCommandHandler(proxyStdIn);
|
||||||
|
startShell(commandHandler);
|
||||||
|
initExecCommand(Shell.getInstance().getOutput());
|
||||||
|
commandHandler.setExecutor(executor, commandLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startShell(ZondCommandHandler commandHandler) {
|
||||||
|
Shell shell = Shell.getInstance();
|
||||||
|
try {
|
||||||
|
shell.setCommandHandler(commandHandler);
|
||||||
|
shell.start(System.in);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initExecCommand(PrintStream stdout) {
|
||||||
|
String cmdLine = Config.getInstance().getString("cmd.start");
|
||||||
|
commandLine = CommandLine.parse(cmdLine);
|
||||||
|
executor = new DefaultExecutor();
|
||||||
|
PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdout, stdout, proxyStdIn);
|
||||||
|
executor.setStreamHandler(pumpStreamHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
67
zond/src/main/java/asys/zond/PingMonitor.java
Normal file
67
zond/src/main/java/asys/zond/PingMonitor.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-07-21
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
public class PingMonitor {
|
||||||
|
private final long step_ping;
|
||||||
|
private final long delayStart;
|
||||||
|
private long lastPingTime;
|
||||||
|
private Thread threadPingMon;
|
||||||
|
private boolean correctShutdown = false;
|
||||||
|
private final int maxlost;
|
||||||
|
|
||||||
|
PingMonitor(int delay, int second, int maxlost) {
|
||||||
|
this.delayStart = delay*1000;
|
||||||
|
this.step_ping = second*1000;
|
||||||
|
this.maxlost = maxlost;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start(final Runnable callback) {
|
||||||
|
this.threadPingMon = new Thread(() -> {
|
||||||
|
correctShutdown = false;
|
||||||
|
try {
|
||||||
|
Thread.sleep(delayStart);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
if ((currentTimeMillis - lastPingTime) >= (maxlost*step_ping)) { // если пропущено N пингов
|
||||||
|
callback.run(); // запускаем код завершения процесса
|
||||||
|
return; // завершаем поток
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(step_ping);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPingTime = 0;
|
||||||
|
});
|
||||||
|
this.threadPingMon.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
if (threadPingMon != null) {
|
||||||
|
threadPingMon.interrupt();
|
||||||
|
threadPingMon = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkPing() {
|
||||||
|
lastPingTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void correctShutdown() {
|
||||||
|
correctShutdown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCorrectShutdown() {
|
||||||
|
return correctShutdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
107
zond/src/main/java/asys/zond/PipeInputStream.java
Normal file
107
zond/src/main/java/asys/zond/PipeInputStream.java
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-07-13
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class PipeInputStream extends InputStream {
|
||||||
|
private byte[] buffer = new byte[1024];
|
||||||
|
private int lastWritePos = 0,
|
||||||
|
lastReadPos = 0,
|
||||||
|
wallPos = buffer.length-1;
|
||||||
|
private boolean closed = false;
|
||||||
|
|
||||||
|
public synchronized void write(String s) {
|
||||||
|
byte[] strBytes = s.getBytes();
|
||||||
|
|
||||||
|
while ((lastWritePos < lastReadPos) && ((lastReadPos - lastWritePos) >= strBytes.length)) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lastWritePos + strBytes.length) >= buffer.length) {
|
||||||
|
wallPos = lastWritePos;
|
||||||
|
lastWritePos = 0;
|
||||||
|
if (lastReadPos == wallPos) {
|
||||||
|
lastReadPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(strBytes, 0, this.buffer, lastWritePos, strBytes.length);
|
||||||
|
lastWritePos += strBytes.length;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int read() throws IOException {
|
||||||
|
if (lastReadPos == lastWritePos) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ignore) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closed) return 0;
|
||||||
|
|
||||||
|
if (lastReadPos == wallPos) {
|
||||||
|
lastReadPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.buffer[lastReadPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return this.read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int read(byte[] buffOut, int off, int len) throws IOException {
|
||||||
|
if (lastReadPos == lastWritePos) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ignore) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closed) return 0;
|
||||||
|
int actualLen = len;
|
||||||
|
|
||||||
|
if (lastReadPos > lastWritePos) {
|
||||||
|
if ((lastReadPos + len) > wallPos) {
|
||||||
|
actualLen = (wallPos - lastReadPos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((lastReadPos + len) > lastWritePos) {
|
||||||
|
actualLen = (lastWritePos - lastReadPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(this.buffer, lastReadPos, buffOut, off, actualLen);
|
||||||
|
lastReadPos += actualLen;
|
||||||
|
if (lastReadPos == wallPos) {
|
||||||
|
lastReadPos = 0;
|
||||||
|
}
|
||||||
|
notify();
|
||||||
|
return actualLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
closed = false;
|
||||||
|
lastWritePos = 0;
|
||||||
|
lastReadPos = 0;
|
||||||
|
wallPos = buffer.length-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() {
|
||||||
|
this.closed = true;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
262
zond/src/main/java/asys/zond/ZondCommandHandler.java
Normal file
262
zond/src/main/java/asys/zond/ZondCommandHandler.java
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <d.mihailov@samson-rus.com>
|
||||||
|
* 2017-06-15
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
import asys.zond.proxy.client.Connector;
|
||||||
|
import asys.zond.proxy.server.Server;
|
||||||
|
import asys.zond.shell.CommandHandler;
|
||||||
|
import asys.zond.shell.Shell;
|
||||||
|
import org.apache.commons.exec.CommandLine;
|
||||||
|
import org.apache.commons.exec.ExecuteException;
|
||||||
|
import org.apache.commons.exec.ExecuteWatchdog;
|
||||||
|
import org.apache.commons.exec.Executor;
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ZondCommandHandler implements CommandHandler {
|
||||||
|
private Executor executor;
|
||||||
|
private CommandLine commandLine;
|
||||||
|
private ExecuteWatchdog watchdog;
|
||||||
|
private Thread threadExec;
|
||||||
|
private Server server;
|
||||||
|
private PipeInputStream proxyStdIn;
|
||||||
|
private PingMonitor pingMonitor;
|
||||||
|
private boolean flagForceRestartProcess = false;
|
||||||
|
private boolean flagManualKill = false;
|
||||||
|
private boolean flagTicker = false;
|
||||||
|
private Thread threadTicker;
|
||||||
|
|
||||||
|
ZondCommandHandler(PipeInputStream proxyStdIn) {
|
||||||
|
this.proxyStdIn = proxyStdIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String zondColored(String string) {
|
||||||
|
return zondColored(string, Ansi.Color.CYAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String zondColored(String string, Ansi.Color color) {
|
||||||
|
return Ansi.ansi().bg(Ansi.Color.WHITE).fg(Ansi.Color.RED).a("[Zond]")
|
||||||
|
.reset().fgBright(color).a(' ').a(string)
|
||||||
|
.reset().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildThreadTicker() {
|
||||||
|
threadTicker = new Thread(() -> {
|
||||||
|
int sec = 0;
|
||||||
|
while (!Thread.currentThread().isInterrupted() && flagTicker) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("Tick "+(sec++)+" sec"));
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "Thread Ticker");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preStartScript() {
|
||||||
|
if (Config.getInstance().getString("cmd.prestart").isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(Arrays.asList(
|
||||||
|
Config.getInstance().getString("cmd.prestart").split(" ", 2)));
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
|
try {
|
||||||
|
Process process = builder.start();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored(line));
|
||||||
|
}
|
||||||
|
process.waitFor();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Shell.getInstance().getOutput()
|
||||||
|
.println(zondColored("[!] PreStart script error: " + e.getMessage(), Ansi.Color.RED));
|
||||||
|
} catch (InterruptedException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void errorStartScript() {
|
||||||
|
if (Config.getInstance().getString("cmd.errorstart").isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(Arrays.asList(
|
||||||
|
Config.getInstance().getString("cmd.errorstart").split(" ", 2)));
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
|
try {
|
||||||
|
Process process = builder.start();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored(line, Ansi.Color.RED));
|
||||||
|
}
|
||||||
|
process.waitFor();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Shell.getInstance().getOutput()
|
||||||
|
.println(zondColored("[!] ErrorStart script error: " + e.getMessage(), Ansi.Color.RED));
|
||||||
|
} catch (InterruptedException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(String commandLine) {
|
||||||
|
if (commandLine.startsWith(":")) {
|
||||||
|
internalCommand(commandLine.substring(1));
|
||||||
|
} else {
|
||||||
|
Shell.getInstance().getOutput().println(commandLine);
|
||||||
|
proxyStdIn.write(commandLine+"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalCommand(String line) {
|
||||||
|
if (line.equalsIgnoreCase("exit") || line.equalsIgnoreCase("quit")) {
|
||||||
|
flagManualKill = true;
|
||||||
|
killProcess();
|
||||||
|
Connector.getInstance().shutdown();
|
||||||
|
Shell.getInstance().shutdown();
|
||||||
|
} else if (line.equalsIgnoreCase("start")) {
|
||||||
|
startProcess();
|
||||||
|
} else if (line.equalsIgnoreCase("kill")) {
|
||||||
|
flagManualKill = true;
|
||||||
|
killProcess();
|
||||||
|
} else if (line.equalsIgnoreCase("connect")) {
|
||||||
|
Connector.getInstance().startReconnect();
|
||||||
|
} else if (line.equalsIgnoreCase("disconnect")) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("Disconnect"));
|
||||||
|
Connector.getInstance().shutdown();
|
||||||
|
} else if (line.equalsIgnoreCase("reload")) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("Reload config"));
|
||||||
|
try {
|
||||||
|
Config.getInstance().load();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else if (line.equalsIgnoreCase("ticker")) {
|
||||||
|
flagTicker = !flagTicker;
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("Ticker " + (flagTicker?"on":"off")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setExecutor(Executor executor, CommandLine commandLine) {
|
||||||
|
this.executor = executor;
|
||||||
|
this.commandLine = commandLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startProcess() {
|
||||||
|
if (watchdog == null || !watchdog.isWatching()) {
|
||||||
|
preStartScript();
|
||||||
|
|
||||||
|
if (watchdog == null) {
|
||||||
|
watchdog = new ZondExecuteWatchdog(
|
||||||
|
ExecuteWatchdog.INFINITE_TIMEOUT,
|
||||||
|
proxyStdIn,
|
||||||
|
Config.getInstance().getString("cmd.killer")
|
||||||
|
);
|
||||||
|
|
||||||
|
executor.setWatchdog(watchdog);
|
||||||
|
}
|
||||||
|
pingMonitor = new PingMonitor(
|
||||||
|
Config.getInstance().getInt("pingmonitor.delay"),
|
||||||
|
Config.getInstance().getInt("bridge.second"),
|
||||||
|
Config.getInstance().getInt("pingmonitor.maxlost")
|
||||||
|
);
|
||||||
|
flagManualKill = false;
|
||||||
|
|
||||||
|
Runnable task = () -> {
|
||||||
|
short _try = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int code;
|
||||||
|
long deadTime = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
server = new Server();
|
||||||
|
server.setPingMonitor(pingMonitor);
|
||||||
|
server.start(Config.getInstance().getInt("bridge.port"));
|
||||||
|
|
||||||
|
deadTime = (System.currentTimeMillis()/1000) + (Config.getInstance().getInt("pingmonitor.delay")*1000);
|
||||||
|
Runnable callback;
|
||||||
|
if (flagTicker) {
|
||||||
|
callback = () -> {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Process - zobie?", Ansi.Color.RED));
|
||||||
|
};
|
||||||
|
|
||||||
|
buildThreadTicker();
|
||||||
|
threadTicker.start();
|
||||||
|
} else {
|
||||||
|
callback = () -> {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Process - zobie?", Ansi.Color.RED));
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Force shutdown process.", Ansi.Color.RED));
|
||||||
|
flagForceRestartProcess = true;
|
||||||
|
killProcess();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pingMonitor.start(callback);
|
||||||
|
code = executor.execute(commandLine);
|
||||||
|
} catch (ExecuteException e) {
|
||||||
|
code = e.getExitValue();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Exception message: " + e.getMessage(), Ansi.Color.RED));
|
||||||
|
code = -99;
|
||||||
|
}
|
||||||
|
long currTime = System.currentTimeMillis()/1000;
|
||||||
|
|
||||||
|
server.shutdown();
|
||||||
|
pingMonitor.stop();
|
||||||
|
server = null;
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[i] Process finished. Code: " + code, Ansi.Color.RED));
|
||||||
|
|
||||||
|
if (pingMonitor.isCorrectShutdown()) {
|
||||||
|
flagForceRestartProcess = false;
|
||||||
|
_try = 0;
|
||||||
|
if (flagTicker)
|
||||||
|
threadTicker.interrupt();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (currTime <= deadTime && !flagManualKill) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Premature end process.", Ansi.Color.RED));
|
||||||
|
_try++;
|
||||||
|
if (_try < 2) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Try start process again...", Ansi.Color.RED));
|
||||||
|
}
|
||||||
|
} else if (flagForceRestartProcess) {
|
||||||
|
_try = 0;
|
||||||
|
flagForceRestartProcess = false;
|
||||||
|
} else if (code != 0 && code != -99 && !flagManualKill) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Try start process again...", Ansi.Color.RED));
|
||||||
|
_try = 0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (_try < 2);
|
||||||
|
|
||||||
|
if (_try == 2) {
|
||||||
|
Shell.getInstance().getOutput().println(zondColored("[!] Discovered the problem when starting the process", Ansi.Color.RED));
|
||||||
|
errorStartScript();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
threadExec = new Thread(task, "Zond Exec");
|
||||||
|
threadExec.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void killProcess() {
|
||||||
|
if (watchdog != null && watchdog.isWatching()) {
|
||||||
|
server.shutdown();
|
||||||
|
pingMonitor.stop();
|
||||||
|
watchdog.destroyProcess();
|
||||||
|
threadExec.interrupt();
|
||||||
|
if (threadTicker != null)
|
||||||
|
threadTicker.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
zond/src/main/java/asys/zond/ZondExecuteWatchdog.java
Normal file
77
zond/src/main/java/asys/zond/ZondExecuteWatchdog.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-07-19
|
||||||
|
*/
|
||||||
|
package asys.zond;
|
||||||
|
|
||||||
|
import asys.zond.shell.Shell;
|
||||||
|
import asys.zond.win32.Kernel32;
|
||||||
|
import asys.zond.win32.W32API;
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
import org.apache.commons.exec.ExecuteWatchdog;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class ZondExecuteWatchdog extends ExecuteWatchdog {
|
||||||
|
private final PipeInputStream inputStream;
|
||||||
|
private long pid;
|
||||||
|
private String cmdkiller;
|
||||||
|
|
||||||
|
ZondExecuteWatchdog(long timeout, PipeInputStream inputStream, String cmdkiller) {
|
||||||
|
super(timeout);
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.cmdkiller = cmdkiller.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long get_pid(Process process) {
|
||||||
|
if (process.getClass().getName().equals("java.lang.Win32Process") ||
|
||||||
|
process.getClass().getName().equals("java.lang.ProcessImpl")) {
|
||||||
|
try {
|
||||||
|
Field f = process.getClass().getDeclaredField("handle");
|
||||||
|
f.setAccessible(true);
|
||||||
|
long handl = f.getLong(process);
|
||||||
|
|
||||||
|
Kernel32 kernel32 = Kernel32.INSTANCE;
|
||||||
|
W32API.HANDLE handle = new W32API.HANDLE();
|
||||||
|
handle.setPointer(Pointer.createConstant(handl));
|
||||||
|
return kernel32.GetProcessId(handle); //pid
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
} else if (process.getClass().getName().equals("java.lang.UNIXProcess")) {
|
||||||
|
try {
|
||||||
|
Field f = process.getClass().getDeclaredField("pid");
|
||||||
|
f.setAccessible(true);
|
||||||
|
return f.getLong(process); //pid
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void start(Process processToMonitor) {
|
||||||
|
super.start(processToMonitor);
|
||||||
|
pid = get_pid(processToMonitor);
|
||||||
|
inputStream.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void stop() {
|
||||||
|
super.stop();
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void destroyProcess() {
|
||||||
|
super.destroyProcess();
|
||||||
|
if (pid != -1 && cmdkiller != null && !cmdkiller.isEmpty()) {
|
||||||
|
try {
|
||||||
|
String cmd = cmdkiller.replace("%PID", String.valueOf(pid));
|
||||||
|
Runtime.getRuntime().exec(cmd);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
zond/src/main/java/asys/zond/proxy/TaskTicker.java
Normal file
52
zond/src/main/java/asys/zond/proxy/TaskTicker.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <d.mihailov@samson-rus.com>
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public TaskTicker setTask(Runnable task) {
|
||||||
|
this.task = task;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskTicker setStepTimeMs(long stepTimeMs) {
|
||||||
|
this.stepTimeMs = stepTimeMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
thread = new Thread(this, "TaskTicker");
|
||||||
|
loop = true;
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
loop = false;
|
||||||
|
if (thread != null) {
|
||||||
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (loop || !Thread.currentThread().isInterrupted()) {
|
||||||
|
task.run();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(stepTimeMs);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
zond/src/main/java/asys/zond/proxy/client/Client.java
Normal file
75
zond/src/main/java/asys/zond/proxy/client/Client.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-16
|
||||||
|
*/
|
||||||
|
package asys.zond.proxy.client;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if (isConnected()) {
|
||||||
|
channelFuture.channel().close();
|
||||||
|
channelFuture = null;
|
||||||
|
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<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||||
|
socketChannel.pipeline().addLast(
|
||||||
|
new PacketEncoder(),
|
||||||
|
new PacketDecoder(),
|
||||||
|
new PacketHandler(),
|
||||||
|
new ClientPacketHandler()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-16
|
||||||
|
*/
|
||||||
|
package asys.zond.proxy.client;
|
||||||
|
|
||||||
|
import asys.mcsmanager.packets.*;
|
||||||
|
import asys.zond.Config;
|
||||||
|
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<Integer, Class<? extends Packet>> handshakePackets;
|
||||||
|
private final Map<Class<? extends Packet>, IPacketHandler> handshakeHandlers;
|
||||||
|
private final BiMap<Integer, Class<? extends Packet>> 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,
|
||||||
|
5, SC_Command.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(
|
||||||
|
Config.getInstance().getString("asys.serverId"),
|
||||||
|
Config.getInstance().getString("asys.passcode")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()));
|
||||||
|
Connector.getInstance().shutdown();
|
||||||
|
} else {
|
||||||
|
context.channel().attr(KNOWN_PACKETS).set(knownPackets);
|
||||||
|
Shell.getInstance().getOutput().println("Handshake: OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
zond/src/main/java/asys/zond/proxy/client/Connector.java
Normal file
64
zond/src/main/java/asys/zond/proxy/client/Connector.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-16
|
||||||
|
*/
|
||||||
|
package asys.zond.proxy.client;
|
||||||
|
|
||||||
|
import asys.mcsmanager.packets.Packet;
|
||||||
|
import asys.zond.Config;
|
||||||
|
import asys.zond.proxy.TaskTicker;
|
||||||
|
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() {
|
||||||
|
if ((connectTicker != null && connectTicker.isActive()) ||
|
||||||
|
(client != null && client.isConnected())) return;
|
||||||
|
client = new Client();
|
||||||
|
connectTicker = new TaskTicker().setStepTimeMs(5000L);
|
||||||
|
connectTicker.setTask(() -> {
|
||||||
|
Shell.getInstance().getOutput()
|
||||||
|
.println(String.format("Connect(%d) to ASys...", ++tryConnect));
|
||||||
|
client.connect(Config.getInstance().getString("asys.host"),
|
||||||
|
Config.getInstance().getInt("asys.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();
|
||||||
|
if (client != null) {
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacket(Packet packet) {
|
||||||
|
if (client != null) {
|
||||||
|
client.sendPacket(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
zond/src/main/java/asys/zond/proxy/server/Server.java
Normal file
63
zond/src/main/java/asys/zond/proxy/server/Server.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-17
|
||||||
|
*/
|
||||||
|
package asys.zond.proxy.server;
|
||||||
|
|
||||||
|
import asys.mcsmanager.packets.codec.PacketDecoder;
|
||||||
|
import asys.mcsmanager.packets.codec.PacketEncoder;
|
||||||
|
import asys.mcsmanager.packets.codec.PacketHandler;
|
||||||
|
import asys.zond.PingMonitor;
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
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.NioServerSocketChannel;
|
||||||
|
|
||||||
|
public class Server {
|
||||||
|
private EventLoopGroup bossGroup, workerGroup;
|
||||||
|
private PingMonitor pingMonitor;
|
||||||
|
|
||||||
|
public void start(int port) {
|
||||||
|
bossGroup = new NioEventLoopGroup(1);
|
||||||
|
workerGroup = new NioEventLoopGroup();
|
||||||
|
|
||||||
|
ServerBootstrap serverBootstrap = createServerBootstrap();
|
||||||
|
serverBootstrap.bind("127.0.0.1", port);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ChannelHandler createChannelInitializer() {
|
||||||
|
return new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||||
|
socketChannel.pipeline().addLast(
|
||||||
|
new PacketEncoder(),
|
||||||
|
new PacketDecoder(),
|
||||||
|
new PacketHandler(),
|
||||||
|
new ServerPacketHandler(pingMonitor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPingMonitor(PingMonitor pingMonitor) {
|
||||||
|
this.pingMonitor = pingMonitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-17
|
||||||
|
*/
|
||||||
|
package asys.zond.proxy.server;
|
||||||
|
|
||||||
|
import asys.mcsmanager.packets.*;
|
||||||
|
import asys.zond.PingMonitor;
|
||||||
|
import asys.zond.proxy.client.Connector;
|
||||||
|
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 ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler {
|
||||||
|
private final BiMap<Integer, Class<? extends Packet>> knownPackets;
|
||||||
|
private final Map<Class<? extends Packet>, IPacketHandler> knownHandlers;
|
||||||
|
private final PingMonitor pingMonitor;
|
||||||
|
|
||||||
|
ServerPacketHandler(PingMonitor pingMonitor) {
|
||||||
|
this.pingMonitor = pingMonitor;
|
||||||
|
knownPackets = ImmutableBiMap.of(
|
||||||
|
3, CS_Ping.class,
|
||||||
|
5, SC_Command.class,
|
||||||
|
6, CS_CorrectShutdown.class
|
||||||
|
);
|
||||||
|
knownHandlers = ImmutableMap.of(
|
||||||
|
CS_Ping.class, this,
|
||||||
|
CS_ConsoleMessage.class, this,
|
||||||
|
CS_CorrectShutdown.class, this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
ctx.channel().attr(KNOWN_PACKETS).set(knownPackets);
|
||||||
|
ctx.channel().attr(KNOWN_HANDLERS).set(knownHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 CS_Ping) {
|
||||||
|
handleCSPing((CS_Ping) packet);
|
||||||
|
} else if (packet instanceof CS_CorrectShutdown) {
|
||||||
|
handleCSCorrectShutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCSPing(CS_Ping packet) {
|
||||||
|
pingMonitor.checkPing();
|
||||||
|
Connector.getInstance().sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCSCorrectShutdown() {
|
||||||
|
pingMonitor.correctShutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
zond/src/main/java/asys/zond/shell/CommandHandler.java
Normal file
9
zond/src/main/java/asys/zond/shell/CommandHandler.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <d.mihailov@samson-rus.com>
|
||||||
|
* 2017-06-15
|
||||||
|
*/
|
||||||
|
package asys.zond.shell;
|
||||||
|
|
||||||
|
public interface CommandHandler {
|
||||||
|
void handle(String commandLine);
|
||||||
|
}
|
||||||
35
zond/src/main/java/asys/zond/shell/CommandLooper.java
Normal file
35
zond/src/main/java/asys/zond/shell/CommandLooper.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-15
|
||||||
|
*/
|
||||||
|
package asys.zond.shell;
|
||||||
|
|
||||||
|
import jline.console.ConsoleReader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CommandLooper implements Runnable {
|
||||||
|
private ConsoleReader consoleReader;
|
||||||
|
private CommandHandler commandHandler;
|
||||||
|
|
||||||
|
CommandLooper(ConsoleReader consoleReader, CommandHandler commandHandler) {
|
||||||
|
this.consoleReader = consoleReader;
|
||||||
|
this.commandHandler = commandHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
String line;
|
||||||
|
try {
|
||||||
|
line = consoleReader.readLine();
|
||||||
|
} catch (IOException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line == null) break;
|
||||||
|
if (line.trim().isEmpty()) continue;
|
||||||
|
|
||||||
|
commandHandler.handle(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
zond/src/main/java/asys/zond/shell/EchoCommandHandler.java
Normal file
12
zond/src/main/java/asys/zond/shell/EchoCommandHandler.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <d.mihailov@samson-rus.com>
|
||||||
|
* 2017-06-15
|
||||||
|
*/
|
||||||
|
package asys.zond.shell;
|
||||||
|
|
||||||
|
public class EchoCommandHandler implements CommandHandler {
|
||||||
|
@Override
|
||||||
|
public void handle(String commandLine) {
|
||||||
|
Shell.getInstance().getOutput().println(commandLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
zond/src/main/java/asys/zond/shell/Shell.java
Normal file
60
zond/src/main/java/asys/zond/shell/Shell.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-06-15
|
||||||
|
*/
|
||||||
|
package asys.zond.shell;
|
||||||
|
|
||||||
|
import jline.console.ConsoleReader;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Shell {
|
||||||
|
private static final String DEFAULT_PROMPT = ":";
|
||||||
|
private static Shell instance;
|
||||||
|
private ConsoleReader consoleReader;
|
||||||
|
private Thread threadCommandLoop;
|
||||||
|
private CommandHandler commandHandler;
|
||||||
|
private ShellStdOut shellStdOut;
|
||||||
|
|
||||||
|
public static Shell getInstance() {
|
||||||
|
if (instance == null) instance = new Shell();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Shell() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(InputStream inputStream) throws IOException {
|
||||||
|
consoleReader = new ConsoleReader(inputStream, (shellStdOut = new ShellStdOut(System.out)));
|
||||||
|
shellStdOut.setConsoleReader(consoleReader);
|
||||||
|
consoleReader.setPrompt(DEFAULT_PROMPT);
|
||||||
|
|
||||||
|
if (commandHandler == null) commandHandler = new EchoCommandHandler();
|
||||||
|
|
||||||
|
threadCommandLoop = new Thread(
|
||||||
|
new CommandLooper(consoleReader, commandHandler),
|
||||||
|
"Zond shell looper");
|
||||||
|
threadCommandLoop.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
threadCommandLoop.interrupt();
|
||||||
|
consoleReader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrompt(String value) {
|
||||||
|
consoleReader.setPrompt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPrompt() {
|
||||||
|
consoleReader.setPrompt(DEFAULT_PROMPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintStream getOutput() {
|
||||||
|
return shellStdOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommandHandler(CommandHandler commandHandler) {
|
||||||
|
this.commandHandler = commandHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
99
zond/src/main/java/asys/zond/shell/ShellStdOut.java
Normal file
99
zond/src/main/java/asys/zond/shell/ShellStdOut.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* DmitriyMX <dimon550@gmail.com>
|
||||||
|
* 2017-07-23
|
||||||
|
*/
|
||||||
|
package asys.zond.shell;
|
||||||
|
|
||||||
|
import jline.console.ConsoleReader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
public class ShellStdOut extends PrintStream {
|
||||||
|
private ConsoleReader consoleReader;
|
||||||
|
|
||||||
|
ShellStdOut(OutputStream out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _write(char c) {
|
||||||
|
try {
|
||||||
|
this.out.write(c);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _write(byte[] buf) {
|
||||||
|
try {
|
||||||
|
this.out.write(buf);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _write(byte[] buf, int off, int len) {
|
||||||
|
try {
|
||||||
|
this.out.write(buf, off, len);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _flush() {
|
||||||
|
try {
|
||||||
|
this.out.flush();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистка печатной строки от мусора
|
||||||
|
*/
|
||||||
|
private void cleanTrashLine(int len) throws IOException {
|
||||||
|
// очищает полностью строку
|
||||||
|
if (consoleReader.getCursorBuffer().buffer.length() + consoleReader.getPrompt().length() > len) {
|
||||||
|
for (int i = len; i <= consoleReader.getCursorBuffer().buffer.length() + 2; i++) {
|
||||||
|
this.out.write(' ');
|
||||||
|
}
|
||||||
|
this.out.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printEx(byte[] buf, int len) {
|
||||||
|
_write(ConsoleReader.RESET_LINE);
|
||||||
|
_write(buf, 0, len);
|
||||||
|
try {
|
||||||
|
cleanTrashLine(len);
|
||||||
|
consoleReader.drawLine();
|
||||||
|
consoleReader.flush();
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buf, int off, int len) {
|
||||||
|
_write(buf, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(String s) {
|
||||||
|
_write(ConsoleReader.RESET_LINE);
|
||||||
|
_write(s.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(String x) {
|
||||||
|
print(x.concat("\n"));
|
||||||
|
try {
|
||||||
|
cleanTrashLine(x.length());
|
||||||
|
consoleReader.drawLine();
|
||||||
|
consoleReader.flush();
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setConsoleReader(ConsoleReader consoleReader) {
|
||||||
|
this.consoleReader = consoleReader;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
zond/src/main/java/asys/zond/win32/Kernel32.java
Normal file
11
zond/src/main/java/asys/zond/win32/Kernel32.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package asys.zond.win32;
|
||||||
|
|
||||||
|
import com.sun.jna.Native;
|
||||||
|
/* https://jna.dev.java.net/ */
|
||||||
|
public interface Kernel32 extends W32API {
|
||||||
|
Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, DEFAULT_OPTIONS);
|
||||||
|
/* http://msdn.microsoft.com/en-us/library/ms683179(VS.85).aspx */
|
||||||
|
HANDLE GetCurrentProcess();
|
||||||
|
/* http://msdn.microsoft.com/en-us/library/ms683215.aspx */
|
||||||
|
int GetProcessId(HANDLE Process);
|
||||||
|
}
|
||||||
65
zond/src/main/java/asys/zond/win32/W32API.java
Normal file
65
zond/src/main/java/asys/zond/win32/W32API.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
package asys.zond.win32;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.sun.jna.*;
|
||||||
|
import com.sun.jna.win32.StdCallLibrary;
|
||||||
|
import com.sun.jna.win32.W32APIFunctionMapper;
|
||||||
|
import com.sun.jna.win32.W32APITypeMapper;
|
||||||
|
|
||||||
|
/** Base type for most W32 API libraries. Provides standard options
|
||||||
|
* for unicode/ASCII mappings. Set the system property w32.ascii
|
||||||
|
* to true to default to the ASCII mappings.
|
||||||
|
*/
|
||||||
|
public interface W32API extends StdCallLibrary, W32Errors {
|
||||||
|
|
||||||
|
/** Standard options to use the unicode version of a w32 API. */
|
||||||
|
Map UNICODE_OPTIONS = new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
|
||||||
|
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Standard options to use the ASCII/MBCS version of a w32 API. */
|
||||||
|
Map ASCII_OPTIONS = new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put(OPTION_TYPE_MAPPER, W32APITypeMapper.ASCII);
|
||||||
|
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.ASCII);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Map DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS;
|
||||||
|
|
||||||
|
class HANDLE extends PointerType {
|
||||||
|
@Override
|
||||||
|
public Object fromNative(Object nativeValue, FromNativeContext context) {
|
||||||
|
Object o = super.fromNative(nativeValue, context);
|
||||||
|
if (INVALID_HANDLE_VALUE.equals(o))
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constant value representing an invalid HANDLE. */
|
||||||
|
HANDLE INVALID_HANDLE_VALUE = new HANDLE() {
|
||||||
|
{ super.setPointer(Pointer.createConstant(-1)); }
|
||||||
|
@Override
|
||||||
|
public void setPointer(Pointer p) {
|
||||||
|
throw new UnsupportedOperationException("Immutable reference");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
23
zond/src/main/java/asys/zond/win32/W32Errors.java
Normal file
23
zond/src/main/java/asys/zond/win32/W32Errors.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
package asys.zond.win32;
|
||||||
|
|
||||||
|
public interface W32Errors {
|
||||||
|
|
||||||
|
int NO_ERROR = 0;
|
||||||
|
int ERROR_INVALID_FUNCTION = 1;
|
||||||
|
int ERROR_FILE_NOT_FOUND = 2;
|
||||||
|
int ERROR_PATH_NOT_FOUND = 3;
|
||||||
|
|
||||||
|
}
|
||||||
157
zond/src/main/java/org/apache/commons/exec/StreamPumper.java
Normal file
157
zond/src/main/java/org/apache/commons/exec/StreamPumper.java
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.commons.exec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import asys.zond.shell.ShellStdOut;
|
||||||
|
import org.apache.commons.exec.util.DebugUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all data from an input stream to an output stream.
|
||||||
|
*
|
||||||
|
* @version $Id: StreamPumper.java 1557263 2014-01-10 21:18:09Z ggregory $
|
||||||
|
*/
|
||||||
|
public class StreamPumper implements Runnable {
|
||||||
|
|
||||||
|
/** the default size of the internal buffer for copying the streams */
|
||||||
|
private static final int DEFAULT_SIZE = 1024;
|
||||||
|
|
||||||
|
/** the input stream to pump from */
|
||||||
|
private final InputStream is;
|
||||||
|
|
||||||
|
/** the output stream to pmp into */
|
||||||
|
private final OutputStream os;
|
||||||
|
|
||||||
|
/** the size of the internal buffer for copying the streams */
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
/** was the end of the stream reached */
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
/** close the output stream when exhausted */
|
||||||
|
private final boolean closeWhenExhausted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new stream pumper.
|
||||||
|
*
|
||||||
|
* @param is input stream to read data from
|
||||||
|
* @param os output stream to write data to.
|
||||||
|
* @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
|
||||||
|
*/
|
||||||
|
public StreamPumper(final InputStream is, final OutputStream os,
|
||||||
|
final boolean closeWhenExhausted) {
|
||||||
|
this.is = is;
|
||||||
|
this.os = os;
|
||||||
|
this.size = DEFAULT_SIZE;
|
||||||
|
this.closeWhenExhausted = closeWhenExhausted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new stream pumper.
|
||||||
|
*
|
||||||
|
* @param is input stream to read data from
|
||||||
|
* @param os output stream to write data to.
|
||||||
|
* @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
|
||||||
|
* @param size the size of the internal buffer for copying the streams
|
||||||
|
*/
|
||||||
|
public StreamPumper(final InputStream is, final OutputStream os,
|
||||||
|
final boolean closeWhenExhausted, final int size) {
|
||||||
|
this.is = is;
|
||||||
|
this.os = os;
|
||||||
|
this.size = size > 0 ? size : DEFAULT_SIZE;
|
||||||
|
this.closeWhenExhausted = closeWhenExhausted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new stream pumper.
|
||||||
|
*
|
||||||
|
* @param is input stream to read data from
|
||||||
|
* @param os output stream to write data to.
|
||||||
|
*/
|
||||||
|
public StreamPumper(final InputStream is, final OutputStream os) {
|
||||||
|
this(is, os, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data from the input stream to the output stream. Terminates as
|
||||||
|
* soon as the input stream is closed or an error occurs.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
synchronized (this) {
|
||||||
|
// Just in case this object is reused in the future
|
||||||
|
finished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] buf = new byte[this.size];
|
||||||
|
|
||||||
|
int length;
|
||||||
|
try {
|
||||||
|
//hack: пропатчили алгоритм
|
||||||
|
while (!Thread.currentThread().isInterrupted() && (length = is.read(buf)) > 0) {
|
||||||
|
if (os instanceof ShellStdOut) {
|
||||||
|
((ShellStdOut)os).printEx(buf, length);
|
||||||
|
} else {
|
||||||
|
os.write(buf, 0, length);
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// nothing to do - happens quite often with watchdog
|
||||||
|
} finally {
|
||||||
|
if (closeWhenExhausted) {
|
||||||
|
try {
|
||||||
|
os.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
final String msg = "Got exception while closing exhausted output stream";
|
||||||
|
DebugUtils.handleException(msg ,e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether the end of the stream has been reached.
|
||||||
|
*
|
||||||
|
* @return true is the stream has been exhausted.
|
||||||
|
*/
|
||||||
|
public synchronized boolean isFinished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method blocks until the stream pumper finishes.
|
||||||
|
*
|
||||||
|
* @exception InterruptedException
|
||||||
|
* if any thread interrupted the current thread before or while the current thread was waiting for a
|
||||||
|
* notification.
|
||||||
|
* @see #isFinished()
|
||||||
|
*/
|
||||||
|
public synchronized void waitFor() throws InterruptedException {
|
||||||
|
while (!isFinished()) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
zond/src/main/resources/zond.properties
Normal file
15
zond/src/main/resources/zond.properties
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
asys.serverId = SpigotServer0
|
||||||
|
asys.host = 127.0.0.1
|
||||||
|
asys.port = 8779
|
||||||
|
asys.passcode = testpassphrase
|
||||||
|
bridge.port = 8710
|
||||||
|
bridge.second = 5
|
||||||
|
pingmonitor.delay = 5
|
||||||
|
pingmonitor.maxlost = 6
|
||||||
|
#Windows
|
||||||
|
#cmd.killer = taskkill /F /PID %PID
|
||||||
|
#Linux
|
||||||
|
cmd.killer = kill -KILL %PID
|
||||||
|
cmd.start = java -jar server.jar
|
||||||
|
cmd.prestart = echo Hello
|
||||||
|
cmd.errorstart = echo Error
|
||||||
Reference in New Issue
Block a user