From 8fb8201e18b34468bbcaa6929093fb2b49148246 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 8 May 2017 23:57:40 +0300 Subject: [PATCH 01/27] =?UTF-8?q?MCSM:=20=D0=BF=D0=BE=D0=B4=D0=B3=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BA=20WebSocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 + .../main/java/asys/mcsmanager/Activator.java | 14 +++-- .../websocket/IndexPageHandler.java | 43 +++++++++++++++ .../asys/mcsmanager/websocket/Server.java | 52 +++++++++++++++++++ 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java create mode 100644 mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index d3ab670..9fb9529 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -16,6 +16,7 @@ jar { '!asys.mcsmanager.packets.*', 'io.netty.buffer;version="[4.0,5)"', 'io.netty.handler.codec;version="[4.0,5)"', + 'io.netty.handler.codec.http;version="[4.0,5)"', '*' } @@ -32,4 +33,5 @@ dependencies { compile project(':webinterface') include files(project(':bridge-protocol').sourceSets.main.output.classesDir) compile group: 'io.netty', name: 'netty-codec', version: nettyVersion + compile group: 'io.netty', name: 'netty-codec-http', version: nettyVersion } diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java b/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java index 91d7f86..f3bc496 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java @@ -5,7 +5,6 @@ package asys.mcsmanager; import asys.api.Config; -import asys.mcsmanager.server.Server; import asys.webinterface.api.Webinterface; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -20,7 +19,8 @@ public class Activator implements BundleActivator, ServiceListener { private ServiceTracker serviceTracker; private MCSM_WebModule module; private Webinterface webinterface; - private Server serverManager; + private asys.mcsmanager.server.Server serverManager; + private asys.mcsmanager.websocket.Server webconsoleServer; @Override public void start(BundleContext context) throws Exception { @@ -43,13 +43,21 @@ public class Activator implements BundleActivator, ServiceListener { int port = config.getInt("asys.mcsmanager.port", 8779); String passcode = config.getString("asys.mcsmanager.passcode", "testpasscode"); logger.debug("Start server manager: {}:{}", host, port); - serverManager = new Server(); + serverManager = new asys.mcsmanager.server.Server(); serverManager.start(host, port, passcode, manager); + + host = config.getString("asys.mcsmanager.webconsole.host", "127.0.0.1"); + port = config.getInt("asys.mcsmanager.webconsole.port", 8770); + logger.debug("Start webconsole server: {}:{}", host, port); + webconsoleServer = new asys.mcsmanager.websocket.Server(); + webconsoleServer.start(host, port); + serviceConfigTracker.close(); } @Override public void stop(BundleContext context) throws Exception { + webconsoleServer.shutdown(); serverManager.shutdown(); if (webinterface != null) { diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java new file mode 100644 index 0000000..a0cd673 --- /dev/null +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java @@ -0,0 +1,43 @@ +/* + * DmitriyMX + * 2017-05-08 + */ +package asys.mcsmanager.websocket; + +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.*; + +public class IndexPageHandler extends ChannelInboundHandlerAdapter { + private static final byte[] CONTENT = "Hello".getBytes(); + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof HttpRequest) { + HttpRequest req = (HttpRequest)msg; + + if (HttpHeaders.is100ContinueExpected(req)) { + ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); + } + boolean keepAlive = HttpHeaders.isKeepAlive(req); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, + Unpooled.wrappedBuffer(CONTENT)); + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); + + if (!keepAlive) { + ctx.write(response).addListener(ChannelFutureListener.CLOSE); + } else { + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + ctx.write(response); + } + } + } +} diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java new file mode 100644 index 0000000..c0fb8b2 --- /dev/null +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java @@ -0,0 +1,52 @@ +/* + * DmitriyMX + * 2017-05-08 + */ +package asys.mcsmanager.websocket; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpServerCodec; + +public class Server { + private EventLoopGroup bossGroup, workerGroup; + + public void start(String host, int port) { + bossGroup = new NioEventLoopGroup(1); + workerGroup = new NioEventLoopGroup(); + + ServerBootstrap serverBootstrap = createServerBootstrap(); + serverBootstrap.bind(host, 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 ChannelInitializer createChannelInitializer() { + return new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + socketChannel.pipeline().addLast( + new HttpServerCodec(), + new IndexPageHandler() + ); + } + }; + } +} From 7294cbc06d2c5bda82c4e453c41d0ea90d86fdfc Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Tue, 9 May 2017 00:56:03 +0300 Subject: [PATCH 02/27] =?UTF-8?q?MCSM:=20=D0=BF=D1=80=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D1=82=D0=B8=D0=BF=20WebSocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mcsmanager/websocket/FrameHandler.java | 49 +++++++++++++++++++ .../websocket/IndexPageHandler.java | 43 ---------------- .../asys/mcsmanager/websocket/Server.java | 6 ++- 3 files changed, 54 insertions(+), 44 deletions(-) create mode 100644 mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java delete mode 100644 mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java new file mode 100644 index 0000000..1d59d20 --- /dev/null +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java @@ -0,0 +1,49 @@ +/* + * DmitriyMX + * 2017-05-09 + */ +package asys.mcsmanager.websocket; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class FrameHandler extends SimpleChannelInboundHandler { + private final Logger logger = LoggerFactory.getLogger(FrameHandler.class); + private ScheduledFuture sesFuture; + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().writeAndFlush(new TextWebSocketFrame("")); + + ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); + sesFuture = ses.scheduleAtFixedRate(() -> ctx.channel().writeAndFlush(new TextWebSocketFrame("")), + 1L, 1L, TimeUnit.SECONDS); + + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + sesFuture.cancel(false); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { + if (frame instanceof TextWebSocketFrame) { + String requestText = ((TextWebSocketFrame)frame).text(); + logger.debug("{} received {}", ctx.channel(), requestText); + ctx.channel().writeAndFlush(new TextWebSocketFrame(""+requestText)); + } else { + logger.warn("unsupport frame type: {}", frame.getClass().getName()); + } + } +} diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java deleted file mode 100644 index a0cd673..0000000 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/IndexPageHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * DmitriyMX - * 2017-05-08 - */ -package asys.mcsmanager.websocket; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.codec.http.*; - -public class IndexPageHandler extends ChannelInboundHandlerAdapter { - private static final byte[] CONTENT = "Hello".getBytes(); - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - HttpRequest req = (HttpRequest)msg; - - if (HttpHeaders.is100ContinueExpected(req)) { - ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); - } - boolean keepAlive = HttpHeaders.isKeepAlive(req); - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.wrappedBuffer(CONTENT)); - response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); - - if (!keepAlive) { - ctx.write(response).addListener(ChannelFutureListener.CLOSE); - } else { - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); - ctx.write(response); - } - } - } -} diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java index c0fb8b2..b73e5d1 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java @@ -10,7 +10,9 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; public class Server { private EventLoopGroup bossGroup, workerGroup; @@ -44,7 +46,9 @@ public class Server { protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast( new HttpServerCodec(), - new IndexPageHandler() + new HttpObjectAggregator(65536), + new WebSocketServerProtocolHandler("/", null, true), + new FrameHandler() ); } }; From 26b5958913f10c8706f9b87f9cb21d0118a63bfe Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Tue, 9 May 2017 18:40:12 +0300 Subject: [PATCH 03/27] =?UTF-8?q?MCSM:=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=82=20Tabs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index f51f1b2..d68238c 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -32,6 +32,19 @@ var NvLineChart = React.createClass({ } }); +var Tabs = React.createClass({ + render: function(){ + var tabsElm = []; + this.props.tabs.forEach(function(title, i){ + tabsElm.push(ce('li', (i === 0 ? {className: 'active'} : null), ce('a', {href: '#'}, title))); + }); + + return( + ce('ul', {className: 'nav nav-tabs'}, tabsElm) + ) + } +}); + var ServerInfo = React.createClass({ getInitialState: function(){return { title: null, @@ -44,6 +57,7 @@ var ServerInfo = React.createClass({ return( ce('div', null, ce('h2', {style: {'margin-top': '0px'}}, this.state.title), + ce(Tabs, {tabs: ['Онлайн1', 'Консоль2']}), ce(NvLineChart, {datum: [{ key: 'Online players', color: '#37d668', From 1085688d1c1092fc192810b852223da5e0892b5e Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Tue, 9 May 2017 19:52:43 +0300 Subject: [PATCH 04/27] =?UTF-8?q?MCSM:Tabs:=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D0=B4=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/components.js | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index d68238c..40bd660 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -33,15 +33,28 @@ var NvLineChart = React.createClass({ }); var Tabs = React.createClass({ + onTabClick: function(idx){ + this.setState({ activeTab: idx }); + }, + /*--------------------*/ + getInitialState: function(){return{ + activeTab: 0 + }}, render: function(){ + var _this = this; + var tabsElm = []; this.props.tabs.forEach(function(title, i){ - tabsElm.push(ce('li', (i === 0 ? {className: 'active'} : null), ce('a', {href: '#'}, title))); + tabsElm.push(ce('li', (i === _this.state.activeTab ? {className: 'active'} : null), + ce('a', {href: '#tab'+(i+1), onClick: _this.onTabClick.bind(_this, i)}, title))); }); - return( - ce('ul', {className: 'nav nav-tabs'}, tabsElm) - ) + var showElement = this.props.children[this.state.activeTab]; + + return(ce('div', null, + ce('ul', {className: 'nav nav-tabs', id: 'tabs'}, tabsElm), + showElement + )) } }); @@ -57,13 +70,15 @@ var ServerInfo = React.createClass({ return( ce('div', null, ce('h2', {style: {'margin-top': '0px'}}, this.state.title), - ce(Tabs, {tabs: ['Онлайн1', 'Консоль2']}), - ce(NvLineChart, {datum: [{ - key: 'Online players', - color: '#37d668', - area: true, - values: this.state.data - }]}) + ce(Tabs, {tabs: ['Онлайн', 'Консоль']}, + ce(NvLineChart, {datum: [{ + key: 'Online players', + color: '#37d668', + area: true, + values: this.state.data + }]}), + ce('p', null, 'fake console element') + ) ) ) } From 86c506dff27118f46a15e81fccd39d8830e8c368 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Tue, 9 May 2017 20:32:45 +0300 Subject: [PATCH 05/27] =?UTF-8?q?MCSM:=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=82=20WebConsole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/asys/mcsmanager/MCSM_WebModule.java | 14 +++++++++++ .../src/main/resources/components.js | 24 ++++++++++++++++++- mcserver-manager/src/main/resources/module.js | 2 ++ .../src/main/resources/moduleStyle.css | 11 +++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 mcserver-manager/src/main/resources/moduleStyle.css diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/MCSM_WebModule.java b/mcserver-manager/src/main/java/asys/mcsmanager/MCSM_WebModule.java index 95fde21..758a1cc 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/MCSM_WebModule.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/MCSM_WebModule.java @@ -26,6 +26,7 @@ public class MCSM_WebModule extends WebModule { private final String MODULE_NAME = "mcsmanager"; private final String MODULE_URL = "/"+MODULE_NAME; private final Pattern URL_PATTERN_JS = Pattern.compile(MODULE_URL+"/(\\w+)\\.js"); + private final Pattern URL_PATTERN_CSS = Pattern.compile(MODULE_URL+"/(\\w+)\\.css"); private Manager manager; MCSM_WebModule(Manager manager) { @@ -66,6 +67,19 @@ public class MCSM_WebModule extends WebModule { this.sendContent(httpExchange, 0, stream); return true; } + + //FIXME дублирование кода + matcher = URL_PATTERN_CSS.matcher(urlPath); + if (matcher.find()) { + InputStream stream = getClass().getResourceAsStream("/" + matcher.group(1) + ".css"); + if (stream == null) { + this.sendHttpCode(httpExchange, 404, "not found"); + return true; + } + httpExchange.getResponseHeaders().add("Content-Type", "text/css;charset=utf-8"); + this.sendContent(httpExchange, 0, stream); + return true; + } } return false; diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 40bd660..8b6cc4d 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -29,6 +29,10 @@ var NvLineChart = React.createClass({ this.d3ChartData.datum(this.props.datum); this.d3ChartData.transition().duration(500).call(this.chart); nv.utils.windowResize(this.chart.update); + }, + componentWillUnmount: function() { + var nvtooltip = document.querySelector('div[class~="nvtooltip"]'); + nvtooltip.parentElement.removeChild(nvtooltip) } }); @@ -58,6 +62,24 @@ var Tabs = React.createClass({ } }); +var WebConsole = React.createClass({ + render: function(){return( + ce('div', {id: 'webconsole'}, + ce('p', null, '[19:50:18 WARN]: **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!'), + ce('p', null, '[19:50:18 WARN]: The server will make no attempt to authenticate usernames. Beware.'), + ce('p', null, '[19:50:18 WARN]: While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.'), + ce('p', null, '[19:50:18 WARN]: To change this, set "online-mode" to "true" in the server.properties file.'), + ce('p', null, '[19:50:18 INFO]: **** Beginning UUID conversion, this may take A LONG time ****'), + ce('p', null, '[19:50:18 INFO]: Preparing level "voidworld"'), + ce('p', null, '[19:50:18 INFO]: -------- World Settings For [voidworld] --------'), + ce('p', null, '[19:50:18 INFO]: Arrow Despawn Rate: 1200'), + ce('p', null, '[19:50:18 INFO]: Item Merge Radius: 2.5'), + ce('p', null, '[19:50:18 INFO]: Item Despawn Rate: 6000'), + ce('p', null, '[19:50:18 INFO]: Allow Zombie Pigmen to spawn from portal blocks: true') + ) + )} +}); + var ServerInfo = React.createClass({ getInitialState: function(){return { title: null, @@ -77,7 +99,7 @@ var ServerInfo = React.createClass({ area: true, values: this.state.data }]}), - ce('p', null, 'fake console element') + ce(WebConsole) ) ) ) diff --git a/mcserver-manager/src/main/resources/module.js b/mcserver-manager/src/main/resources/module.js index 0b42a4b..c712f8c 100644 --- a/mcserver-manager/src/main/resources/module.js +++ b/mcserver-manager/src/main/resources/module.js @@ -60,6 +60,8 @@ var ContentModule = React.createClass({ function(){ _this.setState({nvScriptReady: -5}); console.error('d3 - error'); } ); + loadStyle("/mcsmanager/moduleStyle.css"); + this.requestServerList(); }, render: function(){ diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css new file mode 100644 index 0000000..1c42f17 --- /dev/null +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -0,0 +1,11 @@ +#webconsole { + background-color: #1e1e1e; + color: #eee; + height: 500px; + padding: 8px; + font-family: monospace; +} + +#webconsole p { + margin: 0; +} \ No newline at end of file From 16cdc30d3c1b3d14cfb52ff917a1145632b2f6d2 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Tue, 9 May 2017 20:49:22 +0300 Subject: [PATCH 06/27] =?UTF-8?q?MCSM:fix:=20=D0=BA=D0=BE=D1=80=D1=80?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B5=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20nvtooltip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 8b6cc4d..a8dc8c9 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -32,7 +32,9 @@ var NvLineChart = React.createClass({ }, componentWillUnmount: function() { var nvtooltip = document.querySelector('div[class~="nvtooltip"]'); - nvtooltip.parentElement.removeChild(nvtooltip) + if (nvtooltip !== null) { + nvtooltip.parentElement.removeChild(nvtooltip) + } } }); From 338d7d656b6b14ebe7f4d5b9d069fa163a563745 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 11 May 2017 01:19:12 +0300 Subject: [PATCH 07/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D0=B5=D0=BD=D1=8B=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=81=20websocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mcsmanager/websocket/FrameHandler.java | 8 ++-- .../src/main/resources/components.js | 40 ++++++++++++------- .../src/main/resources/moduleStyle.css | 1 + 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java index 1d59d20..1bc6bc0 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java @@ -19,14 +19,14 @@ import java.util.concurrent.TimeUnit; public class FrameHandler extends SimpleChannelInboundHandler { private final Logger logger = LoggerFactory.getLogger(FrameHandler.class); private ScheduledFuture sesFuture; + private int i = 1; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.channel().writeAndFlush(new TextWebSocketFrame("")); - ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); - sesFuture = ses.scheduleAtFixedRate(() -> ctx.channel().writeAndFlush(new TextWebSocketFrame("")), - 1L, 1L, TimeUnit.SECONDS); + sesFuture = ses.scheduleAtFixedRate(() -> ctx.channel().writeAndFlush( + new TextWebSocketFrame(String.format("", i++))), + 500L, 1000L, TimeUnit.MILLISECONDS); super.channelActive(ctx); } diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index a8dc8c9..26f38ef 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -65,21 +65,33 @@ var Tabs = React.createClass({ }); var WebConsole = React.createClass({ - render: function(){return( - ce('div', {id: 'webconsole'}, - ce('p', null, '[19:50:18 WARN]: **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!'), - ce('p', null, '[19:50:18 WARN]: The server will make no attempt to authenticate usernames. Beware.'), - ce('p', null, '[19:50:18 WARN]: While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.'), - ce('p', null, '[19:50:18 WARN]: To change this, set "online-mode" to "true" in the server.properties file.'), - ce('p', null, '[19:50:18 INFO]: **** Beginning UUID conversion, this may take A LONG time ****'), - ce('p', null, '[19:50:18 INFO]: Preparing level "voidworld"'), - ce('p', null, '[19:50:18 INFO]: -------- World Settings For [voidworld] --------'), - ce('p', null, '[19:50:18 INFO]: Arrow Despawn Rate: 1200'), - ce('p', null, '[19:50:18 INFO]: Item Merge Radius: 2.5'), - ce('p', null, '[19:50:18 INFO]: Item Despawn Rate: 6000'), - ce('p', null, '[19:50:18 INFO]: Allow Zombie Pigmen to spawn from portal blocks: true') + ws: null, + /*--------------------*/ + getInitialState: function(){return{ + lines: [] + }}, + render: function(){ + return( + ce('div', {id: 'webconsole', ref: 'webconsole'}, this.state.lines.map(function(line){ return ce('p', null, line); })) ) - )} + }, + componentDidMount: function(){ + var _this = this; + + this.ws = new WebSocket("ws://127.0.0.1:8770"); //FIXME указывать ip:port из настроек + this.ws.onopen = function(){ console.debug('WS: open...'); }; + this.ws.onclose = function(){ console.debug('WS: close...'); }; + this.ws.onmessage = function(event){ + console.debug('WS: message: '+event.data); + _this.setState({ lines: _this.state.lines.concat([event.data]) }); + }; + }, + componentDidUpdate: function() { + // this.refs.webconsole.scrollTop = this.refs.webconsole.scrollHeight; + }, + componentWillUnmount: function(){ + this.ws.close(); + } }); var ServerInfo = React.createClass({ diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index 1c42f17..76a7bab 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -4,6 +4,7 @@ height: 500px; padding: 8px; font-family: monospace; + overflow-y: scroll; } #webconsole p { From 59010ab902e744f48984d848a70abe3fb3c6404f Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 13 May 2017 01:16:08 +0300 Subject: [PATCH 08/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=81=D0=BE=D0=BB=D1=8C=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=BD=D0=B5=20=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D0=B2=D0=BD=D0=BE=D0=B9=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/components.js | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 26f38ef..1297a8e 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -55,7 +55,13 @@ var Tabs = React.createClass({ ce('a', {href: '#tab'+(i+1), onClick: _this.onTabClick.bind(_this, i)}, title))); }); - var showElement = this.props.children[this.state.activeTab]; + var showElement = this.props.children.map(function(child, i){ + return ce('div', (i !== _this.state.activeTab ? {style: {display: 'none'}} : null), child); + }); + + if (this.props.stateCallback !== null) { + this.props.stateCallback(this.state.activeTab); + } return(ce('div', null, ce('ul', {className: 'nav nav-tabs', id: 'tabs'}, tabsElm), @@ -66,35 +72,45 @@ var Tabs = React.createClass({ var WebConsole = React.createClass({ ws: null, + connect: function(){ + if (this.ws !== null) return; + var _this = this; + + this.ws = new WebSocket("ws://127.0.0.1:8770"); //FIXME указывать ip:port из настроек + this.ws.onopen = function(){ console.debug('WS: open...'); }; + this.ws.onclose = function(){ console.debug('WS: close...'); }; + this.ws.onerror = function(e){ console.debug('WS: error'); console.error(e); }; + this.ws.onmessage = function(event){ + _this.setState({ lines: _this.state.lines.concat([event.data]) }); + }; + }, + disconnect: function() { + if (this.ws === null) return; + this.ws.close(); + this.ws = null; + }, /*--------------------*/ getInitialState: function(){return{ lines: [] }}, render: function(){ return( - ce('div', {id: 'webconsole', ref: 'webconsole'}, this.state.lines.map(function(line){ return ce('p', null, line); })) + ce('div', {id: 'webconsole'}, + this.state.lines.map(function(line){ return ce('p', null, line); })) ) }, - componentDidMount: function(){ - var _this = this; - - this.ws = new WebSocket("ws://127.0.0.1:8770"); //FIXME указывать ip:port из настроек - this.ws.onopen = function(){ console.debug('WS: open...'); }; - this.ws.onclose = function(){ console.debug('WS: close...'); }; - this.ws.onmessage = function(event){ - console.debug('WS: message: '+event.data); - _this.setState({ lines: _this.state.lines.concat([event.data]) }); - }; - }, - componentDidUpdate: function() { - // this.refs.webconsole.scrollTop = this.refs.webconsole.scrollHeight; - }, componentWillUnmount: function(){ - this.ws.close(); + this.disconnect(); } }); var ServerInfo = React.createClass({ + tabStateWebConsole: function(state) { + if (state === 1) { + this.refs.webconsole.connect(); + } + }, + /*--------------------*/ getInitialState: function(){return { title: null, data: [] @@ -106,14 +122,14 @@ var ServerInfo = React.createClass({ return( ce('div', null, ce('h2', {style: {'margin-top': '0px'}}, this.state.title), - ce(Tabs, {tabs: ['Онлайн', 'Консоль']}, + ce(Tabs, {tabs: ['Онлайн', 'Консоль'], stateCallback: this.tabStateWebConsole}, ce(NvLineChart, {datum: [{ key: 'Online players', color: '#37d668', area: true, values: this.state.data }]}), - ce(WebConsole) + ce(WebConsole, {ref: 'webconsole'}) ) ) ) From 305fc3d6b2adaef0de42b547b473313485c91837 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 17 May 2017 17:20:33 +0300 Subject: [PATCH 09/27] =?UTF-8?q?Bridge:=20=D1=82=D0=B5=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D1=85=D0=B2=D0=B0=D1=82=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge/build.gradle | 1 + .../bridge/bukkit/BridgeLoggerAppender.java | 21 +++++++++++++++++++ .../java/asys/bridge/bukkit/BridgePlugin.java | 7 +++++++ 3 files changed, 29 insertions(+) create mode 100644 bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java diff --git a/bridge/build.gradle b/bridge/build.gradle index 28d7e5b..073c68a 100644 --- a/bridge/build.gradle +++ b/bridge/build.gradle @@ -40,4 +40,5 @@ dependencies { exclude group: 'org.avaje' } compile group: 'io.netty', name: 'netty-codec', version: nettyVersion + compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.5' } diff --git a/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java b/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java new file mode 100644 index 0000000..c3c8529 --- /dev/null +++ b/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java @@ -0,0 +1,21 @@ +/* + * DmitriyMX + * 2017-05-17 + */ +package asys.bridge.bukkit; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; + +public class BridgeLoggerAppender extends AbstractAppender { + + BridgeLoggerAppender() { + super("ASysBridge", null, null); + super.start(); + } + + @Override + public void append(LogEvent event) { + System.out.println("+++ "+event.getMessage().getFormattedMessage()); + } +} diff --git a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java index 2f688ba..7e98331 100644 --- a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java +++ b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java @@ -7,6 +7,8 @@ package asys.bridge.bukkit; import asys.bridge.client.Client; import asys.mcsmanager.packets.CS_Ping; import io.netty.channel.Channel; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.Logger; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; @@ -23,6 +25,11 @@ public class BridgePlugin extends JavaPlugin { private ScheduledFuture sesFuture, sesPingFuture; private int tryConnect = 0; + @Override + public void onLoad() { + ((Logger)LogManager.getRootLogger()).addAppender(new BridgeLoggerAppender()); + } + @Override public void onEnable() { INSTANCE = this; From 2070a5eb63f677ba67fdef0cd68105c8bda48974 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 18 May 2017 01:11:51 +0300 Subject: [PATCH 10/27] =?UTF-8?q?Bridge:=20=D1=83=D1=81=D0=BF=D0=B5=D1=88?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=BE=D0=B2=20=D0=BD=D0=B0=20MCSM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge-protocol/build.gradle | 2 +- .../mcsmanager/packets/CS_ConsoleMessage.java | 56 +++++++++++++++++++ bridge/build.gradle | 4 +- .../bridge/bukkit/BridgeLoggerAppender.java | 25 ++++++++- .../java/asys/bridge/bukkit/BridgePlugin.java | 18 ++++-- .../bridge/client/ClientPacketHandler.java | 3 +- mcserver-manager/build.gradle | 2 +- .../server/ServerPacketHandler.java | 13 ++++- 8 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 bridge-protocol/src/main/java/asys/mcsmanager/packets/CS_ConsoleMessage.java diff --git a/bridge-protocol/build.gradle b/bridge-protocol/build.gradle index c53f9d2..6b537fc 100644 --- a/bridge-protocol/build.gradle +++ b/bridge-protocol/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.2-SNAPSHOT' +version = '0.3-SNAPSHOT' task jar(type: Jar, overwrite: true) { // не собирать jar diff --git a/bridge-protocol/src/main/java/asys/mcsmanager/packets/CS_ConsoleMessage.java b/bridge-protocol/src/main/java/asys/mcsmanager/packets/CS_ConsoleMessage.java new file mode 100644 index 0000000..f144589 --- /dev/null +++ b/bridge-protocol/src/main/java/asys/mcsmanager/packets/CS_ConsoleMessage.java @@ -0,0 +1,56 @@ +/* + * DmitriyMX + * 2017-05-17 + */ +package asys.mcsmanager.packets; + +import io.netty.buffer.ByteBuf; + +public class CS_ConsoleMessage extends Packet { + private long time; + private int level; + private String loggerName, + message; + + public CS_ConsoleMessage() { + } + + public CS_ConsoleMessage(long time, int level, String loggerName, String message) { + this.time = time; + this.level = level; + this.loggerName = loggerName; + this.message = message; + } + + public long getTime() { + return time; + } + + public int getLevel() { + return level; + } + + public String getLoggerName() { + return loggerName; + } + + public String getMessage() { + return message; + } + + @Override + public void readSelfData(ByteBuf buffer) { + this.time = buffer.readLong(); + this.level = buffer.readInt(); + this.loggerName = readString(buffer); + this.message = readString(buffer); + } + + @Override + public void writeSelfData(ByteBuf buffer) { + buffer.writeLong(time); + buffer.writeInt(level); + writeString(buffer, loggerName); + writeString(buffer, message); + } +} diff --git a/bridge/build.gradle b/bridge/build.gradle index 073c68a..baa9fe7 100644 --- a/bridge/build.gradle +++ b/bridge/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.5.1-SNAPSHOT' +version = '0.5.2-SNAPSHOT' repositories { maven { url 'https://hub.spigotmc.org/nexus/content/groups/public/' } @@ -40,5 +40,5 @@ dependencies { exclude group: 'org.avaje' } compile group: 'io.netty', name: 'netty-codec', version: nettyVersion - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.5' + compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0-rc1' } diff --git a/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java b/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java index c3c8529..2ab12b2 100644 --- a/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java +++ b/bridge/src/main/java/asys/bridge/bukkit/BridgeLoggerAppender.java @@ -1,13 +1,16 @@ /* - * DmitriyMX + * DmitriyMX * 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); @@ -16,6 +19,24 @@ public class BridgeLoggerAppender extends AbstractAppender { @Override public void append(LogEvent event) { - System.out.println("+++ "+event.getMessage().getFormattedMessage()); + 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; } } diff --git a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java index 7e98331..9223e21 100644 --- a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java +++ b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java @@ -24,18 +24,22 @@ public class BridgePlugin extends JavaPlugin { private ScheduledExecutorService ses; private ScheduledFuture sesFuture, sesPingFuture; private int tryConnect = 0; + private BridgeLoggerAppender loggerAppender; @Override public void onLoad() { - ((Logger)LogManager.getRootLogger()).addAppender(new BridgeLoggerAppender()); + ((Logger)LogManager.getRootLogger()).addAppender(loggerAppender = new BridgeLoggerAppender()); + onEnable(); } @Override public void onEnable() { - INSTANCE = this; - saveDefaultConfig(); + if (INSTANCE == null) { + INSTANCE = this; + saveDefaultConfig(); - startReconnect(); + startReconnect(); + } } @Override @@ -84,6 +88,7 @@ public class BridgePlugin extends JavaPlugin { } public void startPing(Channel channel) { + getLoggerAppender().setChannel(channel); sesPingFuture = ses.scheduleAtFixedRate(() -> { channel.write(new CS_Ping( System.currentTimeMillis(), @@ -104,8 +109,13 @@ public class BridgePlugin extends JavaPlugin { } public void stopPing() { + getLoggerAppender().setChannel(null); if (sesPingFuture != null) { sesPingFuture.cancel(false); } } + + public BridgeLoggerAppender getLoggerAppender() { + return loggerAppender; + } } diff --git a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java index 94c145b..2995740 100644 --- a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java +++ b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java @@ -25,7 +25,8 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements private static Map, IPacketHandler> handshakeHandlers; private static final BiMap> pingPackets = ImmutableBiMap.of( - 3, CS_Ping.class + 3, CS_Ping.class, + 4, CS_ConsoleMessage.class ); ClientPacketHandler() { diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index 9fb9529..1f86fe3 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.6-SNAPSHOT' +version = '0.8.7-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java index 91c9abc..bed9a46 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java @@ -11,6 +11,7 @@ import com.google.common.collect.ImmutableMap; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.AttributeKey; +import org.slf4j.LoggerFactory; import java.util.Map; @@ -26,7 +27,8 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke private static Map, IPacketHandler> handshakeHandlers; private static final BiMap> pingPackets = ImmutableBiMap.of( - 3, CS_Ping.class + 3, CS_Ping.class, + 4, CS_ConsoleMessage.class ); private static Map, IPacketHandler> pingHandlers; @@ -41,7 +43,8 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke if (pingHandlers == null) { pingHandlers = ImmutableMap.of( - CS_Ping.class, this + CS_Ping.class, this, + CS_ConsoleMessage.class, this ); } } @@ -59,6 +62,8 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke handleCSHandshake((CS_Handshake) packet, context); } else if (packet.getClass() == CS_Ping.class) { handleCSPing((CS_Ping) packet, context); + } else if (packet.getClass() == CS_ConsoleMessage.class) { + handleCSConsoleMessage((CS_ConsoleMessage) packet); } } @@ -91,4 +96,8 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke private void handleCSPing(CS_Ping packet, ChannelHandlerContext context) { manager.putInfo(context.channel().attr(CLIENTID).get(), packet); } + + private void handleCSConsoleMessage(CS_ConsoleMessage packet) { + LoggerFactory.getLogger(getClass()).debug("[L:{}] {}", packet.getLevel(), packet.getMessage()); + } } From b1054aab8999c6696fc6b09f42a75196024794ff Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 18 May 2017 14:43:16 +0300 Subject: [PATCH 11/27] =?UTF-8?q?Bridge:fix:=20=D0=B1=D0=B5=D1=81=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D0=B5=D1=87=D0=BD=D1=8B=D0=B9=20=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D0=B5=D0=BA=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge/build.gradle | 2 +- .../java/asys/bridge/bukkit/BridgePlugin.java | 43 +++++++++-------- .../java/asys/bridge/bukkit/TaskTicker.java | 48 +++++++++++++++++++ .../bridge/client/ClientPacketHandler.java | 8 ++-- 4 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 bridge/src/main/java/asys/bridge/bukkit/TaskTicker.java diff --git a/bridge/build.gradle b/bridge/build.gradle index baa9fe7..cd42b01 100644 --- a/bridge/build.gradle +++ b/bridge/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.5.2-SNAPSHOT' +version = '0.5.3-SNAPSHOT' repositories { maven { url 'https://hub.spigotmc.org/nexus/content/groups/public/' } diff --git a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java index 9223e21..be638d7 100644 --- a/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java +++ b/bridge/src/main/java/asys/bridge/bukkit/BridgePlugin.java @@ -13,18 +13,13 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - public class BridgePlugin extends JavaPlugin { public static BridgePlugin INSTANCE; private Client client; - private ScheduledExecutorService ses; - private ScheduledFuture sesFuture, sesPingFuture; + private TaskTicker connectTicker, pingTicker; private int tryConnect = 0; private BridgeLoggerAppender loggerAppender; + private boolean needReconnect = true; @Override public void onLoad() { @@ -44,10 +39,8 @@ public class BridgePlugin extends JavaPlugin { @Override public void onDisable() { - if (sesFuture != null) { - sesFuture.cancel(false); - } - + setNeedReconnect(false); + stopReconnect(); stopPing(); if (client.isConnected()) { @@ -66,8 +59,8 @@ public class BridgePlugin extends JavaPlugin { public void startReconnect() { client = new Client(); - ses = Executors.newScheduledThreadPool(2); - sesFuture = ses.scheduleAtFixedRate(() -> { + connectTicker = new TaskTicker().setStepTimeMs(5000L); + connectTicker.setTask(() -> { getLogger().info(String.format("Connect(%d) to ASys...", ++tryConnect)); client.connect(getConfig().getString("host"), getConfig().getInt("port")); if (client.isConnected()) { @@ -76,20 +69,20 @@ public class BridgePlugin extends JavaPlugin { getLogger().warning( String.format("Connection(%d) fail. Try reconnect...", tryConnect)); } - }, 0L, 5L, TimeUnit.SECONDS); + }).start(); } public void stopReconnect() { - if (sesFuture != null) { - sesFuture.cancel(false); - sesFuture = null; + if (connectTicker != null) { + connectTicker.stop(); tryConnect = 0; } } public void startPing(Channel channel) { getLoggerAppender().setChannel(channel); - sesPingFuture = ses.scheduleAtFixedRate(() -> { + pingTicker = new TaskTicker().setStepTimeMs(5000L); + pingTicker.setTask(() -> { channel.write(new CS_Ping( System.currentTimeMillis(), 20.0D, //FIXME @@ -105,17 +98,25 @@ public class BridgePlugin extends JavaPlugin { getLogger().warning("Try reconnect..."); startReconnect(); } - }, 0L, 5L, TimeUnit.SECONDS); + }).start(); } public void stopPing() { getLoggerAppender().setChannel(null); - if (sesPingFuture != null) { - sesPingFuture.cancel(false); + if (pingTicker != null) { + pingTicker.stop(); } } public BridgeLoggerAppender getLoggerAppender() { return loggerAppender; } + + public boolean isNeedReconnect() { + return needReconnect; + } + + public void setNeedReconnect(boolean needReconnect) { + this.needReconnect = needReconnect; + } } diff --git a/bridge/src/main/java/asys/bridge/bukkit/TaskTicker.java b/bridge/src/main/java/asys/bridge/bukkit/TaskTicker.java new file mode 100644 index 0000000..3799467 --- /dev/null +++ b/bridge/src/main/java/asys/bridge/bukkit/TaskTicker.java @@ -0,0 +1,48 @@ +/* + * DmitriyMX + * 2017-05-18 + */ +package asys.bridge.bukkit; + +public class TaskTicker implements Runnable { + private Runnable task; + private long stepTimeMs = 1000L; + private Thread thread; + private boolean loop = false; + + TaskTicker setTask(Runnable task) { + this.task = task; + return this; + } + + TaskTicker setStepTimeMs(long stepTimeMs) { + this.stepTimeMs = stepTimeMs; + return this; + } + + void start() { + thread = new Thread(this, "TaskTicker"); + loop = true; + thread.start(); + } + + void stop() { + loop = false; + if (thread != null) { + thread.interrupt(); + } + } + + @Override + public void run() { + while (loop || !Thread.currentThread().isInterrupted()) { + task.run(); + + try { + Thread.sleep(stepTimeMs); + } catch (InterruptedException e) { + break; + } + } + } +} diff --git a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java index 2995740..6f34d33 100644 --- a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java +++ b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java @@ -54,10 +54,11 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements @Override public void channelInactive(ChannelHandlerContext context) throws Exception { if (BridgePlugin.INSTANCE != null) { - BridgePlugin.INSTANCE.getLogger().warning("Lost connection!"); BridgePlugin.INSTANCE.stopPing(); - BridgePlugin.INSTANCE.getLogger().warning("Try reconnect..."); - BridgePlugin.INSTANCE.startReconnect(); + if (BridgePlugin.INSTANCE.isNeedReconnect()) { + BridgePlugin.INSTANCE.getLogger().warning("Lost connection! Try reconnect..."); + BridgePlugin.INSTANCE.startReconnect(); + } } context.channel().attr(KNOWN_PACKETS).remove(); context.channel().attr(KNOWN_HANDLERS).remove(); @@ -71,6 +72,7 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements if (pkt.getErrorCode() != 0) { BridgePlugin.INSTANCE.getLogger().severe( String.format("Handshake: #%d %s", pkt.getErrorCode(), pkt.getMessage())); + BridgePlugin.INSTANCE.setNeedReconnect(false); } else { context.channel().attr(KNOWN_PACKETS).set(pingPackets); BridgePlugin.INSTANCE.getLogger().info("Handshake: OK"); From 37b8f02b69b2a9e8c3b61c6668667e09f5109437 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 18 May 2017 15:17:05 +0300 Subject: [PATCH 12/27] =?UTF-8?q?MCSM:=20=D1=83=D1=81=D0=BF=D0=B5=D1=88?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D0=B8=20=D1=81=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=20=D0=B2=20=D0=B1=D1=80=D0=B0=D1=83=D0=B7?= =?UTF-8?q?=D0=B5=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../main/java/asys/mcsmanager/Activator.java | 2 +- .../main/java/asys/mcsmanager/Manager.java | 22 +++++++++++++++++++ .../server/ServerPacketHandler.java | 3 +-- .../mcsmanager/websocket/FrameHandler.java | 15 +++---------- .../asys/mcsmanager/websocket/Server.java | 5 ++++- .../src/main/resources/components.js | 2 +- 7 files changed, 33 insertions(+), 18 deletions(-) diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index 1f86fe3..b63b4e2 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.7-SNAPSHOT' +version = '0.8.8-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java b/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java index f3bc496..676eaff 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/Activator.java @@ -50,7 +50,7 @@ public class Activator implements BundleActivator, ServiceListener { port = config.getInt("asys.mcsmanager.webconsole.port", 8770); logger.debug("Start webconsole server: {}:{}", host, port); webconsoleServer = new asys.mcsmanager.websocket.Server(); - webconsoleServer.start(host, port); + webconsoleServer.start(host, port, manager); serviceConfigTracker.close(); } diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java index e05e6af..5d6f6d5 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java @@ -4,7 +4,10 @@ */ package asys.mcsmanager; +import asys.mcsmanager.packets.CS_ConsoleMessage; import asys.mcsmanager.packets.CS_Ping; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +16,7 @@ import java.util.*; public class Manager { private Logger logger = LoggerFactory.getLogger(Manager.class); private Map serversMap = new HashMap<>(); + private List webconsoleListener = new ArrayList<>(); /** * Добавляем в список ClientID @@ -47,4 +51,22 @@ public class Manager { public ServerInfo getInfo(String clientId) { return serversMap.get(clientId); } + + public void addWebConsoleListener(Channel channel) { + this.webconsoleListener.add(channel); + } + + public void removeWebConsoleListener(Channel channel) { + this.webconsoleListener.remove(channel); + } + + public void sendBroadcastToWebConsoleListeners(CS_ConsoleMessage message) { + for (Channel channel : this.webconsoleListener) { + channel.writeAndFlush(new TextWebSocketFrame(String.format( + "[L:%d] %s", + message.getLevel(), + message.getMessage() + ))); + } + } } diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java index bed9a46..2568af2 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java @@ -11,7 +11,6 @@ import com.google.common.collect.ImmutableMap; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.AttributeKey; -import org.slf4j.LoggerFactory; import java.util.Map; @@ -98,6 +97,6 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke } private void handleCSConsoleMessage(CS_ConsoleMessage packet) { - LoggerFactory.getLogger(getClass()).debug("[L:{}] {}", packet.getLevel(), packet.getMessage()); + manager.sendBroadcastToWebConsoleListeners(packet); } } diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java index 1bc6bc0..74bffe0 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java @@ -11,29 +11,20 @@ import io.netty.handler.codec.http.websocketx.WebSocketFrame; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import static asys.mcsmanager.websocket.Server.manager; public class FrameHandler extends SimpleChannelInboundHandler { private final Logger logger = LoggerFactory.getLogger(FrameHandler.class); - private ScheduledFuture sesFuture; - private int i = 1; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); - sesFuture = ses.scheduleAtFixedRate(() -> ctx.channel().writeAndFlush( - new TextWebSocketFrame(String.format("", i++))), - 500L, 1000L, TimeUnit.MILLISECONDS); - + manager.addWebConsoleListener(ctx.channel()); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - sesFuture.cancel(false); + manager.removeWebConsoleListener(ctx.channel()); } @Override diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java index b73e5d1..a9b30c4 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/Server.java @@ -4,6 +4,7 @@ */ package asys.mcsmanager.websocket; +import asys.mcsmanager.Manager; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; @@ -15,9 +16,11 @@ import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; public class Server { + static Manager manager; private EventLoopGroup bossGroup, workerGroup; - public void start(String host, int port) { + public void start(String host, int port, Manager manager) { + Server.manager = manager; bossGroup = new NioEventLoopGroup(1); workerGroup = new NioEventLoopGroup(); diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 1297a8e..15c13aa 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -81,7 +81,7 @@ var WebConsole = React.createClass({ this.ws.onclose = function(){ console.debug('WS: close...'); }; this.ws.onerror = function(e){ console.debug('WS: error'); console.error(e); }; this.ws.onmessage = function(event){ - _this.setState({ lines: _this.state.lines.concat([event.data]) }); + _this.setState({ lines: _this.state.lines.concat([event.data]) }); //TODO необходимо ограничить кол-во строк }; }, disconnect: function() { From c208c9f0155f17c43c73e39487bb6e6a9a56398b Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 18 May 2017 16:09:37 +0300 Subject: [PATCH 13/27] =?UTF-8?q?MCSM:=20=D0=BF=D1=80=D0=B5=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20AnsiCo?= =?UTF-8?q?lor->HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../src/main/resources/ansi_up.js | 333 ++++++++++++++++++ .../src/main/resources/components.js | 4 +- mcserver-manager/src/main/resources/module.js | 5 + 4 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 mcserver-manager/src/main/resources/ansi_up.js diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index b63b4e2..7b37dcb 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.8-SNAPSHOT' +version = '0.8.9-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/resources/ansi_up.js b/mcserver-manager/src/main/resources/ansi_up.js new file mode 100644 index 0000000..4d695c2 --- /dev/null +++ b/mcserver-manager/src/main/resources/ansi_up.js @@ -0,0 +1,333 @@ +/* ansi_up.js + * author : Dru Nelson + * license : MIT + * http://github.com/drudru/ansi_up + */ +(function (factory) { + var v; + if (typeof module === "object" && typeof module.exports === "object") { + v = factory(require, exports); + if ("undefined" !== typeof v) module.exports = v; + } + else if ("function" === typeof define && define.amd) { + define(["require", "exports"], factory); + } + else { + var req, exp = {}; + v = factory(req, exp); + window.AnsiUp = exp.default; + } +})(function (require, exports) { + +"use strict"; +function rgx(tmplObj) { + var subst = []; + for (var _i = 1; _i < arguments.length; _i++) { + subst[_i - 1] = arguments[_i]; + } + var regexText = tmplObj.raw[0]; + var wsrgx = /^\s+|\s+\n|\s+#[\s\S]+?\n/gm; + var txt2 = regexText.replace(wsrgx, ''); + return new RegExp(txt2, 'm'); +} +var AnsiUp = (function () { + function AnsiUp() { + this.VERSION = "2.0.0"; + this.ansi_colors = [ + [ + { rgb: [0, 0, 0], class_name: "ansi-black" }, + { rgb: [187, 0, 0], class_name: "ansi-red" }, + { rgb: [0, 187, 0], class_name: "ansi-green" }, + { rgb: [187, 187, 0], class_name: "ansi-yellow" }, + { rgb: [0, 0, 187], class_name: "ansi-blue" }, + { rgb: [187, 0, 187], class_name: "ansi-magenta" }, + { rgb: [0, 187, 187], class_name: "ansi-cyan" }, + { rgb: [255, 255, 255], class_name: "ansi-white" } + ], + [ + { rgb: [85, 85, 85], class_name: "ansi-bright-black" }, + { rgb: [255, 85, 85], class_name: "ansi-bright-red" }, + { rgb: [0, 255, 0], class_name: "ansi-bright-green" }, + { rgb: [255, 255, 85], class_name: "ansi-bright-yellow" }, + { rgb: [85, 85, 255], class_name: "ansi-bright-blue" }, + { rgb: [255, 85, 255], class_name: "ansi-bright-magenta" }, + { rgb: [85, 255, 255], class_name: "ansi-bright-cyan" }, + { rgb: [255, 255, 255], class_name: "ansi-bright-white" } + ] + ]; + this.htmlFormatter = { + transform: function (fragment, instance) { + var txt = fragment.text; + if (txt.length === 0) + return txt; + if (instance._escape_for_html) + txt = instance.old_escape_for_html(txt); + if (!fragment.bright && fragment.fg === null && fragment.bg === null) + return txt; + var styles = []; + var classes = []; + var fg = fragment.fg; + var bg = fragment.bg; + if (fg === null && fragment.bright) + fg = instance.ansi_colors[1][7]; + if (!instance._use_classes) { + if (fg) + styles.push("color:rgb(" + fg.rgb.join(',') + ")"); + if (bg) + styles.push("background-color:rgb(" + bg.rgb + ")"); + } + else { + if (fg) { + if (fg.class_name !== 'truecolor') { + classes.push(fg.class_name + "-fg"); + } + else { + styles.push("color:rgb(" + fg.rgb.join(',') + ")"); + } + } + if (bg) { + if (bg.class_name !== 'truecolor') { + classes.push(bg.class_name + "-bg"); + } + else { + styles.push("background-color:rgb(" + bg.rgb.join(',') + ")"); + } + } + } + var class_string = ''; + var style_string = ''; + if (classes.length) + class_string = " class=\"" + classes.join(' ') + "\""; + if (styles.length) + style_string = " style=\"" + styles.join(';') + "\""; + return "" + txt + ""; + }, + compose: function (segments, instance) { + return segments.join(""); + } + }; + this.textFormatter = { + transform: function (fragment, instance) { + return fragment.text; + }, + compose: function (segments, instance) { + return segments.join(""); + } + }; + this.setup_256_palette(); + this._use_classes = false; + this._escape_for_html = true; + this.bright = false; + this.fg = this.bg = null; + this._buffer = ''; + } + Object.defineProperty(AnsiUp.prototype, "use_classes", { + get: function () { + return this._use_classes; + }, + set: function (arg) { + this._use_classes = arg; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(AnsiUp.prototype, "escape_for_html", { + get: function () { + return this._escape_for_html; + }, + set: function (arg) { + this._escape_for_html = arg; + }, + enumerable: true, + configurable: true + }); + AnsiUp.prototype.setup_256_palette = function () { + var _this = this; + this.palette_256 = []; + this.ansi_colors.forEach(function (palette) { + palette.forEach(function (rec) { + _this.palette_256.push(rec); + }); + }); + var levels = [0, 95, 135, 175, 215, 255]; + for (var r = 0; r < 6; ++r) { + for (var g = 0; g < 6; ++g) { + for (var b = 0; b < 6; ++b) { + var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' }; + this.palette_256.push(col); + } + } + } + var grey_level = 8; + for (var i = 0; i < 24; ++i, grey_level += 10) { + var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' }; + this.palette_256.push(gry); + } + }; + AnsiUp.prototype.old_escape_for_html = function (txt) { + return txt.replace(/[&<>]/gm, function (str) { + if (str === "&") + return "&"; + if (str === "<") + return "<"; + if (str === ">") + return ">"; + }); + }; + AnsiUp.prototype.old_linkify = function (txt) { + return txt.replace(/(https?:\/\/[^\s]+)/gm, function (str) { + return "" + str + ""; + }); + }; + AnsiUp.prototype.detect_incomplete_ansi = function (txt) { + return !(/.*?[\x40-\x7e]/.test(txt)); + }; + AnsiUp.prototype.detect_incomplete_link = function (txt) { + var found = false; + for (var i = txt.length - 1; i > 0; i--) { + if (/\s|\x1B/.test(txt[i])) { + found = true; + break; + } + } + if (!found) { + if (/(https?:\/\/[^\s]+)/.test(txt)) + return 0; + else + return -1; + } + var prefix = txt.substr(i + 1, 4); + if (prefix.length === 0) + return -1; + if ("http".indexOf(prefix) === 0) + return (i + 1); + }; + AnsiUp.prototype.ansi_to = function (txt, formatter) { + var pkt = this._buffer + txt; + this._buffer = ''; + var raw_text_pkts = pkt.split(/\x1B\[/); + if (raw_text_pkts.length === 1) + raw_text_pkts.push(''); + this.handle_incomplete_sequences(raw_text_pkts); + var first_chunk = this.with_state(raw_text_pkts.shift()); + var blocks = new Array(raw_text_pkts.length); + for (var i = 0, len = raw_text_pkts.length; i < len; ++i) { + blocks[i] = (formatter.transform(this.process_ansi(raw_text_pkts[i]), this)); + } + if (first_chunk.text.length > 0) + blocks.unshift(formatter.transform(first_chunk, this)); + return formatter.compose(blocks, this); + }; + AnsiUp.prototype.ansi_to_html = function (txt) { + return this.ansi_to(txt, this.htmlFormatter); + }; + AnsiUp.prototype.ansi_to_text = function (txt) { + return this.ansi_to(txt, this.textFormatter); + }; + AnsiUp.prototype.with_state = function (text) { + return { bright: this.bright, fg: this.fg, bg: this.bg, text: text }; + }; + AnsiUp.prototype.handle_incomplete_sequences = function (chunks) { + var last_chunk = chunks[chunks.length - 1]; + if ((last_chunk.length > 0) && this.detect_incomplete_ansi(last_chunk)) { + this._buffer = "\x1B[" + last_chunk; + chunks.pop(); + chunks.push(''); + } + else { + if (last_chunk.slice(-1) === "\x1B") { + this._buffer = "\x1B"; + console.log("raw", chunks); + chunks.pop(); + chunks.push(last_chunk.substr(0, last_chunk.length - 1)); + console.log(chunks); + console.log(last_chunk); + } + if (chunks.length === 2 && + chunks[1] === "" && + chunks[0].slice(-1) === "\x1B") { + this._buffer = "\x1B"; + last_chunk = chunks.shift(); + chunks.unshift(last_chunk.substr(0, last_chunk.length - 1)); + } + } + }; + AnsiUp.prototype.process_ansi = function (block) { + if (!this._sgr_regex) { + this._sgr_regex = (_a = ["\n ^ # beginning of line\n ([!<-?]?) # a private-mode char (!, <, =, >, ?)\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n ([sS]*) # any text following this CSI sequence\n "], _a.raw = ["\n ^ # beginning of line\n ([!\\x3c-\\x3f]?) # a private-mode char (!, <, =, >, ?)\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n ([\\s\\S]*) # any text following this CSI sequence\n "], rgx(_a)); + } + var matches = block.match(this._sgr_regex); + if (!matches) { + return this.with_state(block); + } + var orig_txt = matches[4]; + if (matches[1] !== '' || matches[3] !== 'm') { + return this.with_state(orig_txt); + } + var sgr_cmds = matches[2].split(';'); + while (sgr_cmds.length > 0) { + var sgr_cmd_str = sgr_cmds.shift(); + var num = parseInt(sgr_cmd_str, 10); + if (isNaN(num) || num === 0) { + this.fg = this.bg = null; + this.bright = false; + } + else if (num === 1) { + this.bright = true; + } + else if (num === 39) { + this.fg = null; + } + else if (num === 49) { + this.bg = null; + } + else if ((num >= 30) && (num < 38)) { + var bidx = this.bright ? 1 : 0; + this.fg = this.ansi_colors[bidx][(num - 30)]; + } + else if ((num >= 90) && (num < 98)) { + this.fg = this.ansi_colors[1][(num - 90)]; + } + else if ((num >= 40) && (num < 48)) { + this.bg = this.ansi_colors[0][(num - 40)]; + } + else if ((num >= 100) && (num < 108)) { + this.bg = this.ansi_colors[1][(num - 100)]; + } + else if (num === 38 || num === 48) { + if (sgr_cmds.length > 0) { + var is_foreground = (num === 38); + var mode_cmd = sgr_cmds.shift(); + if (mode_cmd === '5' && sgr_cmds.length > 0) { + var palette_index = parseInt(sgr_cmds.shift(), 10); + if (palette_index >= 0 && palette_index <= 255) { + if (is_foreground) + this.fg = this.palette_256[palette_index]; + else + this.bg = this.palette_256[palette_index]; + } + } + if (mode_cmd === '2' && sgr_cmds.length > 2) { + var r = parseInt(sgr_cmds.shift(), 10); + var g = parseInt(sgr_cmds.shift(), 10); + var b = parseInt(sgr_cmds.shift(), 10); + if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) { + var c = { rgb: [r, g, b], class_name: 'truecolor' }; + if (is_foreground) + this.fg = c; + else + this.bg = c; + } + } + } + } + } + return this.with_state(orig_txt); + var _a; + }; + return AnsiUp; +}()); +//# sourceMappingURL=ansi_up.js.map + Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = AnsiUp; +}); \ No newline at end of file diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 15c13aa..9b83965 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -94,9 +94,11 @@ var WebConsole = React.createClass({ lines: [] }}, render: function(){ + var ansi_up = new AnsiUp; + return( ce('div', {id: 'webconsole'}, - this.state.lines.map(function(line){ return ce('p', null, line); })) + this.state.lines.map(function(line){ return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); })) ) }, componentWillUnmount: function(){ diff --git a/mcserver-manager/src/main/resources/module.js b/mcserver-manager/src/main/resources/module.js index c712f8c..d8c052b 100644 --- a/mcserver-manager/src/main/resources/module.js +++ b/mcserver-manager/src/main/resources/module.js @@ -60,6 +60,11 @@ var ContentModule = React.createClass({ function(){ _this.setState({nvScriptReady: -5}); console.error('d3 - error'); } ); + loadScript("/mcsmanager/ansi_up.js", + function(){ console.debug('ansi_up - ok'); }, + function(){ console.debug('ansi_up - error'); } + ); + loadStyle("/mcsmanager/moduleStyle.css"); this.requestServerList(); From b148a8c3d038730a4beaa5f6ba125b3f6a00e35d Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 3 Jun 2017 16:09:06 +0300 Subject: [PATCH 14/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=B5=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../src/main/resources/components.js | 8 +++++++- .../src/main/resources/moduleStyle.css | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index 7b37dcb..c629a24 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.9-SNAPSHOT' +version = '0.8.10-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 9b83965..4c721b7 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -98,7 +98,13 @@ var WebConsole = React.createClass({ return( ce('div', {id: 'webconsole'}, - this.state.lines.map(function(line){ return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); })) + ce('div', {className: 'output'}, + this.state.lines.map(function(line){ + return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); + }) + ), + ce('input') + ) ) }, componentWillUnmount: function(){ diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index 76a7bab..8622552 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -1,4 +1,4 @@ -#webconsole { +#webconsole .output { background-color: #1e1e1e; color: #eee; height: 500px; @@ -7,6 +7,18 @@ overflow-y: scroll; } -#webconsole p { +#webconsole .output p { margin: 0; +} + +#webconsole input { + background-color: #1e1e1e; + color: #eee; + border: none; + padding: 8px; + width: 100%; +} + +#webconsole input:focus { + outline: none; } \ No newline at end of file From 9b66cc558868c13ff45eb788a1a2e1088d1548ff Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 3 Jun 2017 16:37:38 +0300 Subject: [PATCH 15/27] =?UTF-8?q?MCSM:Tabs:=20callback=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D1=8F=D1=8E=D1=82=D1=81=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D0=BB=D1=8E=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 4c721b7..dc94b32 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -59,14 +59,15 @@ var Tabs = React.createClass({ return ce('div', (i !== _this.state.activeTab ? {style: {display: 'none'}} : null), child); }); - if (this.props.stateCallback !== null) { - this.props.stateCallback(this.state.activeTab); - } - return(ce('div', null, ce('ul', {className: 'nav nav-tabs', id: 'tabs'}, tabsElm), showElement )) + }, + componentDidUpdate: function() { + if (this.props.stateCallback !== null) { + this.props.stateCallback(this.state.activeTab); + } } }); From 80ff52b471f828f72213cfbed1cfd7c8ee526e15 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 3 Jun 2017 16:41:27 +0300 Subject: [PATCH 16/27] =?UTF-8?q?MCSM:WebConsole:=20=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BB=D0=B8=D1=81=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BC=D0=BE=D0=B4=D0=B5=D1=80=D0=BD=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=8F=20=D0=B2=D0=B2?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 6 +++++- mcserver-manager/src/main/resources/moduleStyle.css | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index dc94b32..b4d5570 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -90,6 +90,9 @@ var WebConsole = React.createClass({ this.ws.close(); this.ws = null; }, + focusInput: function() { + this.refs.input.focus(); + }, /*--------------------*/ getInitialState: function(){return{ lines: [] @@ -104,7 +107,7 @@ var WebConsole = React.createClass({ return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); }) ), - ce('input') + ce('input', {ref: 'input'}) ) ) }, @@ -117,6 +120,7 @@ var ServerInfo = React.createClass({ tabStateWebConsole: function(state) { if (state === 1) { this.refs.webconsole.connect(); + this.refs.webconsole.focusInput(); } }, /*--------------------*/ diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index 8622552..a66d2b5 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -13,9 +13,11 @@ #webconsole input { background-color: #1e1e1e; + background-image: url('data:image/svg+xml;utf-8,>'); + background-repeat: no-repeat; color: #eee; border: none; - padding: 8px; + padding: 8px 8px 8px 1.5em; width: 100%; } From 3e3b0d3602be5bdfb2e5b4e659a995e1aba60461 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 3 Jun 2017 16:55:10 +0300 Subject: [PATCH 17/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BE=D1=82=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B0?= =?UTF-8?q?=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index b4d5570..e838fb4 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -93,6 +93,13 @@ var WebConsole = React.createClass({ focusInput: function() { this.refs.input.focus(); }, + handleKeyInput: function(event) { + if (event.key === 'Enter') { + console.debug("send command '" + this.refs.input.value + "'"); + this.ws.send(':'+this.refs.input.value); + this.refs.input.value = ''; + } + }, /*--------------------*/ getInitialState: function(){return{ lines: [] @@ -107,7 +114,7 @@ var WebConsole = React.createClass({ return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); }) ), - ce('input', {ref: 'input'}) + ce('input', {ref: 'input', 'onKeyPress': this.handleKeyInput}) ) ) }, From 4db11619988974736294372e3a0320f1eee6a2ae Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 5 Jun 2017 11:10:26 +0300 Subject: [PATCH 18/27] =?UTF-8?q?MCSM:=20=D0=A1=D0=BE=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B8=20=D1=81=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/asys/mcsmanager/Manager.java | 10 +++++++++- .../asys/mcsmanager/server/ServerPacketHandler.java | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java index 5d6f6d5..032af85 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java @@ -16,6 +16,7 @@ import java.util.*; public class Manager { private Logger logger = LoggerFactory.getLogger(Manager.class); private Map serversMap = new HashMap<>(); + private Map serverChannels = new HashMap<>(); private List webconsoleListener = new ArrayList<>(); /** @@ -23,16 +24,23 @@ public class Manager { * @param clientId id сервера. Чувствителен к регистру * @return false, если сервер с таким id уже имеется */ - public boolean addClientId(String clientId) { + public boolean addClientId(String clientId, Channel channel) { if (serversMap.containsKey(clientId)) { return false; } logger.debug("addClientId: {}", clientId); serversMap.put(clientId, new ServerInfo(clientId)); + serverChannels.put(clientId, channel); return true; } + public void removeClientId(String clientId) { + if (clientId != null) { + serverChannels.remove(clientId); + } + } + /** * Дополнить информация о сервере. * Если сервер отсутствует в списке, информация будет потеряна. diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java index 2568af2..fa25259 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java @@ -55,6 +55,12 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke super.channelActive(context); } + @Override + public void channelInactive(ChannelHandlerContext context) throws Exception { + manager.removeClientId(context.channel().attr(CLIENTID).get()); + super.channelInactive(context); + } + @Override public void handle(Packet packet, ChannelHandlerContext context) { if (packet.getClass() == CS_Handshake.class) { @@ -76,7 +82,7 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke return; } - if (!manager.addClientId(packet.getClientId())) { + if (!manager.addClientId(packet.getClientId(), context.channel())) { try { context.channel().writeAndFlush(HandshakeResult.CLIENTID_EXISTS).sync().channel().close(); } catch (InterruptedException ignore) { From bffe5d2ed3976b47928eb2a8779f5c9fedbcb4a5 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 5 Jun 2017 12:52:04 +0300 Subject: [PATCH 19/27] =?UTF-8?q?Bridge-protocol:=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=BF=D1=83=D1=81=D0=BA=20=D0=BD=D0=B5=20=D0=B8=D0=B7=D0=B2?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BD=D1=8B=D1=85=20=D0=BF=D0=B0=D0=BA=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge-protocol/build.gradle | 2 +- .../java/asys/mcsmanager/packets/codec/PacketDecoder.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bridge-protocol/build.gradle b/bridge-protocol/build.gradle index 6b537fc..1518b24 100644 --- a/bridge-protocol/build.gradle +++ b/bridge-protocol/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.3-SNAPSHOT' +version = '0.3.1-SNAPSHOT' task jar(type: Jar, overwrite: true) { // не собирать jar diff --git a/bridge-protocol/src/main/java/asys/mcsmanager/packets/codec/PacketDecoder.java b/bridge-protocol/src/main/java/asys/mcsmanager/packets/codec/PacketDecoder.java index ea926a8..cdcd832 100644 --- a/bridge-protocol/src/main/java/asys/mcsmanager/packets/codec/PacketDecoder.java +++ b/bridge-protocol/src/main/java/asys/mcsmanager/packets/codec/PacketDecoder.java @@ -19,12 +19,15 @@ public class PacketDecoder extends ReplayingDecoder { protected void decode(ChannelHandlerContext contect, ByteBuf inBuf, List out) throws Exception { int id = inBuf.readUnsignedByte(); Class pktClass = contect.channel().attr(KNOWN_PACKETS).get().get(id); - if (pktClass == null) return; + if (pktClass == null) return; //TODO надо бы в логгере писать про отсутствующий пакет if (contect.channel().attr(KNOWN_HANDLERS).get().containsKey(pktClass)) { Packet packet = pktClass.newInstance(); packet.readSelfData(inBuf); out.add(packet); + } else { + //TODO по хорошему, надо информровать, что отсутствует обработчик пакета + inBuf.skipBytes(inBuf.readableBytes()); } } } From fd3174bf6304936964638ef341f134f45e1e7989 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 5 Jun 2017 12:53:00 +0300 Subject: [PATCH 20/27] =?UTF-8?q?Bridge-protocol:=20=D0=BF=D0=B0=D0=BA?= =?UTF-8?q?=D0=B5=D1=82=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B0=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge-protocol/build.gradle | 2 +- .../asys/mcsmanager/packets/SC_Command.java | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 bridge-protocol/src/main/java/asys/mcsmanager/packets/SC_Command.java diff --git a/bridge-protocol/build.gradle b/bridge-protocol/build.gradle index 1518b24..44006f7 100644 --- a/bridge-protocol/build.gradle +++ b/bridge-protocol/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.3.1-SNAPSHOT' +version = '0.4-SNAPSHOT' task jar(type: Jar, overwrite: true) { // не собирать jar diff --git a/bridge-protocol/src/main/java/asys/mcsmanager/packets/SC_Command.java b/bridge-protocol/src/main/java/asys/mcsmanager/packets/SC_Command.java new file mode 100644 index 0000000..1cca1bb --- /dev/null +++ b/bridge-protocol/src/main/java/asys/mcsmanager/packets/SC_Command.java @@ -0,0 +1,32 @@ +/* + * DmitriyMX + * 2017-06-05 + */ +package asys.mcsmanager.packets; + +import io.netty.buffer.ByteBuf; + +public class SC_Command extends Packet { + private String command; + + public SC_Command() { + } + + public SC_Command(String command) { + this.command = command; + } + + public String getCommand() { + return command; + } + + @Override + public void readSelfData(ByteBuf buffer) { + command = readString(buffer); + } + + @Override + public void writeSelfData(ByteBuf buffer) { + writeString(buffer, command); + } +} From d57db71789b5eeceae45af01106849b5f2817954 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 5 Jun 2017 12:54:14 +0300 Subject: [PATCH 21/27] =?UTF-8?q?MCSM:=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=20(need=20fixed)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../src/main/java/asys/mcsmanager/Manager.java | 16 ++++++++++++++++ .../mcsmanager/server/ServerPacketHandler.java | 3 ++- .../asys/mcsmanager/websocket/FrameHandler.java | 6 ++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index c629a24..b45f816 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.10-SNAPSHOT' +version = '0.8.11-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java index 032af85..4f06e68 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/Manager.java @@ -6,6 +6,7 @@ package asys.mcsmanager; import asys.mcsmanager.packets.CS_ConsoleMessage; import asys.mcsmanager.packets.CS_Ping; +import asys.mcsmanager.packets.SC_Command; import io.netty.channel.Channel; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.slf4j.Logger; @@ -41,6 +42,21 @@ public class Manager { } } + public boolean sendCommand(String clientId, String command) { +/* + if (serverChannels.containsKey(clientId)) { + serverChannels.get(clientId).writeAndFlush(new SC_Command(command)); + return true; + } else { + return false; + } +*/ + //FIXME временный костыль + Channel channel = serverChannels.entrySet().iterator().next().getValue(); + channel.writeAndFlush(new SC_Command(command)); + return true; + } + /** * Дополнить информация о сервере. * Если сервер отсутствует в списке, информация будет потеряна. diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java index fa25259..241a64f 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/server/ServerPacketHandler.java @@ -27,7 +27,8 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke private static final BiMap> pingPackets = ImmutableBiMap.of( 3, CS_Ping.class, - 4, CS_ConsoleMessage.class + 4, CS_ConsoleMessage.class, + 5, SC_Command.class ); private static Map, IPacketHandler> pingHandlers; diff --git a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java index 74bffe0..1dbd305 100644 --- a/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java +++ b/mcserver-manager/src/main/java/asys/mcsmanager/websocket/FrameHandler.java @@ -31,8 +31,10 @@ public class FrameHandler extends SimpleChannelInboundHandler { protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { if (frame instanceof TextWebSocketFrame) { String requestText = ((TextWebSocketFrame)frame).text(); - logger.debug("{} received {}", ctx.channel(), requestText); - ctx.channel().writeAndFlush(new TextWebSocketFrame(""+requestText)); + if (requestText.startsWith(":")) { + //FIXME убрать костыли + manager.sendCommand(null, requestText.substring(1)); + } } else { logger.warn("unsupport frame type: {}", frame.getClass().getName()); } From 97fb820dd363c80ef0e8c7218800f609b738d3f5 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 5 Jun 2017 12:54:44 +0300 Subject: [PATCH 22/27] =?UTF-8?q?Bridge:=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B0?= =?UTF-8?q?=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge/build.gradle | 2 +- .../bridge/client/ClientPacketHandler.java | 27 +++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/bridge/build.gradle b/bridge/build.gradle index cd42b01..9c830d9 100644 --- a/bridge/build.gradle +++ b/bridge/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.5.3-SNAPSHOT' +version = '0.6-SNAPSHOT' repositories { maven { url 'https://hub.spigotmc.org/nexus/content/groups/public/' } diff --git a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java index 6f34d33..0360d8e 100644 --- a/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java +++ b/bridge/src/main/java/asys/bridge/client/ClientPacketHandler.java @@ -26,13 +26,15 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements private static final BiMap> pingPackets = ImmutableBiMap.of( 3, CS_Ping.class, - 4, CS_ConsoleMessage.class + 4, CS_ConsoleMessage.class, + 5, SC_Command.class ); ClientPacketHandler() { if (handshakeHandlers == null) { handshakeHandlers = ImmutableMap.of( - SC_HandshakeResult.class, this + SC_HandshakeResult.class, this, + SC_Command.class, this ); } } @@ -68,10 +70,17 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements @Override public void handle(Packet packet, ChannelHandlerContext context) { BridgePlugin.INSTANCE.getLogger().info("handle : " + packet.getClass().getSimpleName()); - SC_HandshakeResult pkt = (SC_HandshakeResult) packet; - if (pkt.getErrorCode() != 0) { + if (packet instanceof SC_HandshakeResult) { + handleHandshakeResult((SC_HandshakeResult) packet, context); + } else if (packet instanceof SC_Command) { + handleCommand((SC_Command) packet); + } + } + + private void handleHandshakeResult(SC_HandshakeResult packet, ChannelHandlerContext context) { + if (packet.getErrorCode() != 0) { BridgePlugin.INSTANCE.getLogger().severe( - String.format("Handshake: #%d %s", pkt.getErrorCode(), pkt.getMessage())); + String.format("Handshake: #%d %s", packet.getErrorCode(), packet.getMessage())); BridgePlugin.INSTANCE.setNeedReconnect(false); } else { context.channel().attr(KNOWN_PACKETS).set(pingPackets); @@ -79,4 +88,12 @@ public class ClientPacketHandler extends ChannelInboundHandlerAdapter implements BridgePlugin.INSTANCE.startPing(context.channel()); } } + + private void handleCommand(SC_Command packet) { + BridgePlugin.INSTANCE.getLogger().info("Command: " + packet.getCommand()); + BridgePlugin.INSTANCE.getServer().dispatchCommand( + BridgePlugin.INSTANCE.getServer().getConsoleSender(), + packet.getCommand() + ); + } } From 7458fdde419fa69c0799da7e5a02d25a8227182b Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 7 Jun 2017 02:00:25 +0300 Subject: [PATCH 23/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BA=D0=B0=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D0=BC=D0=BD=D1=8B=D0=B9=20=D1=81=D0=BA=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../src/main/resources/components.js | 65 ++++++++++++++++++- .../src/main/resources/moduleStyle.css | 31 ++++++++- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index b45f816..083c88e 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.11-SNAPSHOT' +version = '0.8.12-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index e838fb4..eb95959 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -100,6 +100,37 @@ var WebConsole = React.createClass({ this.refs.input.value = ''; } }, + // raf: window.requestAnimationFrame //FIXME + // || window.webkitRequestAnimationFrame + // || window.mozRequestAnimationFrame + // || window.msRequestAnimationFrame + // || function(cb) { return window.setTimeout(cb, 1000 / 60); }, + scrollRatio: 0, + lastPageY: 0, + handleScrollMouseDown: function(event) { + this.lastPageY = event.pageY; + document.addEventListener('mousemove', this.handleScrollMouseMove); + document.addEventListener('mouseup', this.handleScrollMouseUp); + }, + handleScrollMouseMove: function(event) { + var delta = event.pageY - this.lastPageY; + this.lastPageY = event.pageY; + + var _this = this; + // this.raf(function(){ //FIXME + window.requestAnimationFrame(function(){ + var totalHeight = _this.refs.content.scrollHeight; + var ownHeight = _this.refs.content.clientHeight; + _this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; + + _this.refs.content.scrollTop += delta / _this.scrollRatio; + _this.refs.scroll.style.top = (_this.refs.content.scrollTop / totalHeight) * 100 + "%"; + }); + }, + handleScrollMouseUp: function(event) { + document.removeEventListener('mousemove', this.handleScrollMouseMove); + document.removeEventListener('mouseup', this.handleScrollMouseUp); + }, /*--------------------*/ getInitialState: function(){return{ lines: [] @@ -110,14 +141,42 @@ var WebConsole = React.createClass({ return( ce('div', {id: 'webconsole'}, ce('div', {className: 'output'}, - this.state.lines.map(function(line){ - return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); - }) + ce('div', {className: 'scrollContent'}, + ce('div', {className: 'content', ref: 'content'}, + this.state.lines.map(function(line){ + return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); + }) + ), + ), + ce('div', {className: 'scroll', ref: 'scroll'}) ), ce('input', {ref: 'input', 'onKeyPress': this.handleKeyInput}) ) ) }, + componentDidMount: function() { + this.refs.scroll.addEventListener('mousedown', this.handleScrollMouseDown); + + var totalHeight = this.refs.content.scrollHeight; + var ownHeight = this.refs.content.clientHeight; + this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; + this.scrollRatio = ownHeight / totalHeight; + + var _this = this; + this.refs.content.addEventListener('scroll', function(){ + // this.raf(function(){ //FIXME + window.requestAnimationFrame(function(){ + var totalHeight = _this.refs.content.scrollHeight; + _this.refs.scroll.style.top = (_this.refs.content.scrollTop / totalHeight) * 100 + "%"; + }); + }); + }, + componentDidUpdate: function() { + var totalHeight = this.refs.content.scrollHeight; + var ownHeight = this.refs.content.clientHeight; + this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; + this.scrollRatio = ownHeight / totalHeight; + }, componentWillUnmount: function(){ this.disconnect(); } diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index a66d2b5..3dbd1e8 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -1,16 +1,41 @@ #webconsole .output { background-color: #1e1e1e; color: #eee; - height: 500px; + min-height: 500px; + height: 1px; padding: 8px; font-family: monospace; - overflow-y: scroll; + position: relative; } -#webconsole .output p { +#webconsole .output .scrollContent { + overflow: hidden; + height: 100%; +} + +#webconsole .output .scrollContent .content { + overflow: auto; + height: 100%; + position: relative; + right: -18px; + margin-left: -18px; +} + +#webconsole .output .scrollContent .content p { margin: 0; } +#webconsole .output .scroll { + width: 9px; + background: #f00; + position: absolute; + top: 0; + height: 21.0836%; + cursor: -webkit-grab; + cursor: -moz-grab; + right: 0; +} + #webconsole input { background-color: #1e1e1e; background-image: url('data:image/svg+xml;utf-8,>'); From 177e66c00ddc9801e5867b9fef81e4de30e14aaf Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 7 Jun 2017 11:58:20 +0300 Subject: [PATCH 24/27] fix javascript --- mcserver-manager/src/main/resources/components.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index eb95959..2484a9a 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -146,7 +146,7 @@ var WebConsole = React.createClass({ this.state.lines.map(function(line){ return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); }) - ), + ) ), ce('div', {className: 'scroll', ref: 'scroll'}) ), From 8e361fab2b64cd0298dcd549d0cfc11ae29b1976 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 7 Jun 2017 13:30:39 +0300 Subject: [PATCH 25/27] =?UTF-8?q?MCSM:WebConsole:=20=D1=80=D0=B5=D1=84?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=81=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D0=BB=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/build.gradle | 2 +- .../src/main/resources/components.js | 125 +++++++++--------- .../src/main/resources/moduleStyle.css | 6 +- 3 files changed, 66 insertions(+), 67 deletions(-) diff --git a/mcserver-manager/build.gradle b/mcserver-manager/build.gradle index 083c88e..e47c984 100644 --- a/mcserver-manager/build.gradle +++ b/mcserver-manager/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.8.12-SNAPSHOT' +version = '0.8.13-SNAPSHOT' apply plugin: 'osgi' diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 2484a9a..70bb06f 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -71,6 +71,64 @@ var Tabs = React.createClass({ } }); +var ScrollingContent = React.createClass({ + totalHeight: 0, + ownHeight: 0, + scrollRatio: 0, + lastPageY: 0, + updateScrollParams: function () { + this.totalHeight = this.refs.content.scrollHeight; + this.ownHeight = this.refs.content.clientHeight; + this.scrollRatio = this.ownHeight / this.totalHeight; + if (isNaN(this.scrollRatio)) this.scrollRatio = 0; + this.refs.scroll.style.height = this.scrollRatio * 100 + "%"; + }, + handleScrollMouseDown: function (event) { + this.lastPageY = event.pageY; + document.addEventListener('mousemove', this.handleScrollMouseMove); + document.addEventListener('mouseup', this.handleScrollMouseUp); + }, + handleScrollMouseMove: function (event) { + var delta = event.pageY - this.lastPageY; + this.lastPageY = event.pageY; + + var _this = this; + window.requestAnimationFrame(function(){ + _this.refs.content.scrollTop += delta / _this.scrollRatio; + _this.refs.scroll.style.top = (_this.refs.content.scrollTop / _this.totalHeight) * 100 + "%"; + }); + }, + handleScrollMouseUp: function (event) { + document.removeEventListener('mousemove', this.handleScrollMouseMove); + document.removeEventListener('mouseup', this.handleScrollMouseUp); + }, + /*--------------------*/ + render: function () { + return ( + ce('div', {className: this.props.className}, + ce('div', {className: 'wrapper'}, + ce('div', {className: 'content', ref: 'content'}, this.props.children) + ), + ce('div', {className: 'scroll', ref: 'scroll'}) + ) + ); + }, + componentDidMount: function () { + this.updateScrollParams(); + this.refs.scroll.addEventListener('mousedown', this.handleScrollMouseDown); + + var _this = this; + this.refs.content.addEventListener('scroll', function(){ + window.requestAnimationFrame(function(){ + _this.refs.scroll.style.top = (_this.refs.content.scrollTop / _this.totalHeight) * 100 + "%"; + }); + }); + }, + componentDidUpdate: function () { + this.updateScrollParams(); + } +}); + var WebConsole = React.createClass({ ws: null, connect: function(){ @@ -100,37 +158,6 @@ var WebConsole = React.createClass({ this.refs.input.value = ''; } }, - // raf: window.requestAnimationFrame //FIXME - // || window.webkitRequestAnimationFrame - // || window.mozRequestAnimationFrame - // || window.msRequestAnimationFrame - // || function(cb) { return window.setTimeout(cb, 1000 / 60); }, - scrollRatio: 0, - lastPageY: 0, - handleScrollMouseDown: function(event) { - this.lastPageY = event.pageY; - document.addEventListener('mousemove', this.handleScrollMouseMove); - document.addEventListener('mouseup', this.handleScrollMouseUp); - }, - handleScrollMouseMove: function(event) { - var delta = event.pageY - this.lastPageY; - this.lastPageY = event.pageY; - - var _this = this; - // this.raf(function(){ //FIXME - window.requestAnimationFrame(function(){ - var totalHeight = _this.refs.content.scrollHeight; - var ownHeight = _this.refs.content.clientHeight; - _this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; - - _this.refs.content.scrollTop += delta / _this.scrollRatio; - _this.refs.scroll.style.top = (_this.refs.content.scrollTop / totalHeight) * 100 + "%"; - }); - }, - handleScrollMouseUp: function(event) { - document.removeEventListener('mousemove', this.handleScrollMouseMove); - document.removeEventListener('mouseup', this.handleScrollMouseUp); - }, /*--------------------*/ getInitialState: function(){return{ lines: [] @@ -140,43 +167,15 @@ var WebConsole = React.createClass({ return( ce('div', {id: 'webconsole'}, - ce('div', {className: 'output'}, - ce('div', {className: 'scrollContent'}, - ce('div', {className: 'content', ref: 'content'}, - this.state.lines.map(function(line){ - return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); - }) - ) - ), - ce('div', {className: 'scroll', ref: 'scroll'}) + ce(ScrollingContent, {className: 'output'}, + this.state.lines.map(function(line){ + return ce('p', {dangerouslySetInnerHTML: {__html: ansi_up.ansi_to_html(line)}}); + }) ), ce('input', {ref: 'input', 'onKeyPress': this.handleKeyInput}) ) ) }, - componentDidMount: function() { - this.refs.scroll.addEventListener('mousedown', this.handleScrollMouseDown); - - var totalHeight = this.refs.content.scrollHeight; - var ownHeight = this.refs.content.clientHeight; - this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; - this.scrollRatio = ownHeight / totalHeight; - - var _this = this; - this.refs.content.addEventListener('scroll', function(){ - // this.raf(function(){ //FIXME - window.requestAnimationFrame(function(){ - var totalHeight = _this.refs.content.scrollHeight; - _this.refs.scroll.style.top = (_this.refs.content.scrollTop / totalHeight) * 100 + "%"; - }); - }); - }, - componentDidUpdate: function() { - var totalHeight = this.refs.content.scrollHeight; - var ownHeight = this.refs.content.clientHeight; - this.refs.scroll.style.height = (ownHeight / totalHeight) * 100 + "%"; - this.scrollRatio = ownHeight / totalHeight; - }, componentWillUnmount: function(){ this.disconnect(); } diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index 3dbd1e8..b3f7b82 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -8,12 +8,12 @@ position: relative; } -#webconsole .output .scrollContent { +#webconsole .output .wrapper { overflow: hidden; height: 100%; } -#webconsole .output .scrollContent .content { +#webconsole .output .wrapper .content { overflow: auto; height: 100%; position: relative; @@ -21,7 +21,7 @@ margin-left: -18px; } -#webconsole .output .scrollContent .content p { +#webconsole .output .wrapper .content p { margin: 0; } From e8084f5d458ef2f4f711c352041c6a2157886c23 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 7 Jun 2017 14:26:49 +0300 Subject: [PATCH 26/27] =?UTF-8?q?MCSM:WebConsole:=20=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=D0=B0=20=D1=81=D0=BA?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D0=BB=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcserver-manager/src/main/resources/components.js | 11 +++++++++++ mcserver-manager/src/main/resources/moduleStyle.css | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/mcserver-manager/src/main/resources/components.js b/mcserver-manager/src/main/resources/components.js index 70bb06f..5e9e803 100644 --- a/mcserver-manager/src/main/resources/components.js +++ b/mcserver-manager/src/main/resources/components.js @@ -81,10 +81,17 @@ var ScrollingContent = React.createClass({ this.ownHeight = this.refs.content.clientHeight; this.scrollRatio = this.ownHeight / this.totalHeight; if (isNaN(this.scrollRatio)) this.scrollRatio = 0; + this.refs.scroll.style.height = this.scrollRatio * 100 + "%"; + this.refs.scroll.style.top = (this.refs.content.scrollTop / this.totalHeight) * 100 + "%"; }, + toggleSelect: function (value, event) { return value; }, handleScrollMouseDown: function (event) { this.lastPageY = event.pageY; + document.body.classList.add('scroll-grabbed'); + this.refs.scroll.classList.add('scroll-grabbed'); + + document.onselectstart = this.toggleSelect.bind(null, false); document.addEventListener('mousemove', this.handleScrollMouseMove); document.addEventListener('mouseup', this.handleScrollMouseUp); }, @@ -99,6 +106,10 @@ var ScrollingContent = React.createClass({ }); }, handleScrollMouseUp: function (event) { + document.body.classList.remove('scroll-grabbed'); + this.refs.scroll.classList.remove('scroll-grabbed'); + + document.onselectstart = this.toggleSelect.bind(null, true); document.removeEventListener('mousemove', this.handleScrollMouseMove); document.removeEventListener('mouseup', this.handleScrollMouseUp); }, diff --git a/mcserver-manager/src/main/resources/moduleStyle.css b/mcserver-manager/src/main/resources/moduleStyle.css index b3f7b82..063e814 100644 --- a/mcserver-manager/src/main/resources/moduleStyle.css +++ b/mcserver-manager/src/main/resources/moduleStyle.css @@ -36,6 +36,12 @@ right: 0; } +.scroll-grabbed, +.scroll-grabbed * { + cursor: -webkit-grabbing !important; + cursor: -moz-grabbing !important; +} + #webconsole input { background-color: #1e1e1e; background-image: url('data:image/svg+xml;utf-8,>'); From 6526553d2c8f026518f542bf476b26158535a35b Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 7 Jun 2017 14:32:49 +0300 Subject: [PATCH 27/27] =?UTF-8?q?Webinterface:=20=D0=BA=D0=BE=D1=80=D1=80?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D1=83=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webinterface/build.gradle | 2 +- webinterface/src/main/resources/index.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/webinterface/build.gradle b/webinterface/build.gradle index b10045b..198e942 100644 --- a/webinterface/build.gradle +++ b/webinterface/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.18.3-SNAPSHOT' +version = '0.18.4-SNAPSHOT' buildscript { repositories { diff --git a/webinterface/src/main/resources/index.html b/webinterface/src/main/resources/index.html index 7bc0e36..b70d987 100644 --- a/webinterface/src/main/resources/index.html +++ b/webinterface/src/main/resources/index.html @@ -2,6 +2,7 @@ + ASys: Web interface