From 96481032c5875273af5c0341e5e21764fc6d9523 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Sun, 10 Nov 2019 10:11:13 +0800 Subject: [PATCH] feat: update websocket server Signed-off-by: MiaoWoo --- packages/plugins/package.json | 1 + packages/plugins/public/index.html | 5 +- packages/plugins/public/js/editor.js | 51 ++++---- packages/plugins/src/WebSocket.ts | 169 +++++++++++++++------------ 4 files changed, 126 insertions(+), 100 deletions(-) diff --git a/packages/plugins/package.json b/packages/plugins/package.json index ea029208..28698935 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@ms/plugin": "^0.1.1", + "axios": "^0.19.0", "es6-map": "^0.1.5", "inversify": "^5.0.1" } diff --git a/packages/plugins/public/index.html b/packages/plugins/public/index.html index 52b8f28a..9ed867bf 100644 --- a/packages/plugins/public/index.html +++ b/packages/plugins/public/index.html @@ -10,6 +10,7 @@ MiaoConsole 调试工具 + @@ -58,11 +59,11 @@
+ aria-valuemax="100" :css="{width: (@classes.loaded/@classes.total*100) +'%', 'min-width': '20em'}"> 正在加载 {{@type}} 类型定义 {{@classes.loaded}}/{{@classes.total}}({{~~(@classes.loaded/@classes.total*100)|percent}}%)
-
+
代码编辑器
diff --git a/packages/plugins/public/js/editor.js b/packages/plugins/public/js/editor.js index 2c7890a8..bd36fb31 100644 --- a/packages/plugins/public/js/editor.js +++ b/packages/plugins/public/js/editor.js @@ -4,29 +4,32 @@ let codeStorageKey = "MiaoScript:code"; let monaco_path = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.18.1/min' require.config({ paths: { 'vs': monaco_path + '/vs' } }); window.MonacoEnvironment = { getWorkerUrl: () => proxy }; -let proxy = URL.createObjectURL(new Blob([` - self.MonacoEnvironment = { - baseUrl: '${monaco_path}/' - }; - importScripts('${monaco_path}/vs/base/worker/workerMain.js'); -`], { type: 'text/javascript' })); +let proxy = URL.createObjectURL(new Blob([`self.MonacoEnvironment = {baseUrl: '${monaco_path}/'}; +importScripts('${monaco_path}/vs/base/worker/workerMain.js');`], { type: 'text/javascript' })); -require(["vs/editor/editor.main"], function() { - if (main.type !== 'unknow') { - let ts_d_src = `https://cdn.jsdelivr.net/gh/circlecloud/ms@master/packages/${main.type}/src/typings` - $.get(`${ts_d_src}/index.ts`, (res) => { - monaco.languages.typescript.javascriptDefaults.addExtraLib(res, 'file:///src/typings/index.ts') - let classes = res.split('\n').map(line => line.match(/.*\.\/(.*)".*/)).filter(line => line).map(dts => dts[1]) - main.classes.total = classes.length - main.classes.loaded = 0 - classes.forEach(fname => { - $.get(`${ts_d_src}/${fname}`, content => { - monaco.languages.typescript.javascriptDefaults.addExtraLib(content, `file:///src/typings/${fname}`) - main.classes.loaded++ - }) - }) - }) - } +function loadExtraLibs(ts_d_src, filter) { + let count = 0; + axios.get(`${ts_d_src}/index.d.ts`).then(async result => { + monaco.languages.typescript.javascriptDefaults.addExtraLib(result.data, 'file:///src/typings/index.d.ts') + let classes = result.data.split('\n').map(line => line.match(/.*\.\/(.*)".*/)).filter(line => line).map(dts => dts[1]) + if (filter) { + classes = classes.filter(line => filter(line)) + } + main.classes.total += classes.length + for (let fname of classes) { + if (count++ % 50 == 0) { await axios.get(`${ts_d_src}/${fname}`) } + loadExtraLib(`${ts_d_src}/${fname}`, `file:///src/typings/${fname}`) + } + }) +} + +function loadExtraLib(url, file) { + axios.get(url).then(result => monaco.languages.typescript.javascriptDefaults.addExtraLib(result.data, file)).finally(() => main.classes.loaded++) +} + +require(["vs/editor/editor.main"], async function() { + main.classes.total = 0 + main.classes.loaded = 0 editor = monaco.editor.create(document.getElementById('editor'), { value: window.localStorage.getItem(codeStorageKey) || 'org.bukkit.Bukkit.server.version', language: 'javascript', @@ -47,6 +50,10 @@ require(["vs/editor/editor.main"], function() { editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_Q, function() { console.log('switch') }) + loadExtraLibs(`https://cdn.jsdelivr.net/gh/circlecloud/ms@master/packages/types/dist/typings/jdk`, (line) => line.startsWith('java.lang')) + if (main.type !== 'unknow') { + loadExtraLibs(`https://cdn.jsdelivr.net/gh/circlecloud/ms@master/packages/types/dist/typings/${main.type}`) + } }); function getSelectContent(editor) { let selInfo = editor.getSelection(); diff --git a/packages/plugins/src/WebSocket.ts b/packages/plugins/src/WebSocket.ts index 84de6ed4..49fcd934 100644 --- a/packages/plugins/src/WebSocket.ts +++ b/packages/plugins/src/WebSocket.ts @@ -1,64 +1,70 @@ +/// +/// import { plugin as pluginApi, server } from '@ms/api' import { plugin, interfaces, cmd } from '@ms/plugin' -import { DefaultContainer as container, inject, postConstruct } from '@ms/container'; +import { DefaultContainer as container, inject, postConstruct } from '@ms/container' import * as reflect from '@ms/common/dist/reflect' -let clients: any[] = []; +let clients: any[] = [] let SPLIT_LINE = '\\M\\W\\S|T|S|S/L/T/' const refList: Array<{ server: string, future: string }> = [ { server: 'an', future: 'g' }, { server: 'getServerConnection', future: 'f' }, { server: 'func_147137_ag', future: 'field_151274_e' } -]; +] + +const Callable = Java.type('java.util.concurrent.Callable') @plugin({ name: 'WebSocket', version: '1.0.0', author: 'MiaoWoo', source: __filename }) export class WebSocket extends interfaces.Plugin { @inject(pluginApi.PluginManager) - private PluginManager: pluginApi.PluginManager; + private PluginManager: pluginApi.PluginManager @inject(server.ServerType) - private ServerType: string; - private pipeline: any; + private ServerType: string + @inject(pluginApi.PluginInstance) + private pluginInstance: any + private pipeline: any @cmd() ws(sender: any, command: string, args: string[]) { switch (args[0]) { case "reload": - this.PluginManager.reload(this); - break; + this.PluginManager.reload(this) + break default: } } disable() { if (this.pipeline) { - this.pipeline.remove('miao_detect'); + this.pipeline.remove('miao_detect') clients.forEach(c => c.close()) container.unbind('onmessage') } } bukkitenable() { - let Bukkit = Java.type('org.bukkit.Bukkit'); - let consoleServer = reflect.on(Bukkit.getServer()).get('console').get(); + let Bukkit = Java.type('org.bukkit.Bukkit') + let consoleServer = reflect.on(Bukkit.getServer()).get('console').get() this.injectMiaoDetect(this.reflectPromise(consoleServer)) } spongeenable() { - let Sponge = Java.type('org.spongepowered.api.Sponge'); - let consoleServer = reflect.on(Sponge.getServer()).get(); + let Sponge = Java.type('org.spongepowered.api.Sponge') + let consoleServer = reflect.on(Sponge.getServer()).get() this.injectMiaoDetect(this.reflectPromise(consoleServer)) } reflectPromise(consoleServer) { for (const ref of refList) { - try { return reflect.on(consoleServer).call(ref.server).get(ref.future).get().get(0); } catch (error) { } + try { return reflect.on(consoleServer).call(ref.server).get(ref.future).get().get(0) } catch (error) { } } } injectMiaoDetect(promise) { - if (!promise) { throw Error(`Can't found ServerConnection or ChannelFuture !`) }; - this.pipeline = reflect.on(promise).get('channel').get().pipeline(); - this.pipeline.addFirst('miao_detect', new MiaoDetectHandler()); + if (!promise) { throw Error(`Can't found ServerConnection or ChannelFuture !`) } + this.pipeline = reflect.on(promise).get('channel').get().pipeline() + this.pipeline.addFirst('miao_detect', new MiaoDetectHandler()) container.bind('onmessage').toFunction(this.onmessage.bind(this)) } @@ -74,20 +80,31 @@ export class WebSocket extends interfaces.Plugin { } execCommand(ctx: any, cmd: string) { - org.bukkit.Bukkit.dispatchCommand(org.bukkit.Bukkit.getConsoleSender(), cmd) + switch (this.ServerType) { + case "bukkit": + org.bukkit.Bukkit.dispatchCommand(org.bukkit.Bukkit.getConsoleSender(), cmd) + break + case "sponge": + break + } return `§6命令: §b${cmd} §a执行成功!` } execCode(ctx: any, code: string) { - return eval(code) || '无返回结果' + switch (this.ServerType) { + case "bukkit": + return org.bukkit.Bukkit.getScheduler().callSyncMethod(this.pluginInstance, new Callable({ call: () => eval(code) })).get() || '无返回结果' + case "sponge": + return '' + } } execDetect(ctx: any, cmd: string) { switch (cmd) { case "type": - let version = this.ServerType == 'bukkit' ? org.bukkit.Bukkit.getServer().getVersion() : org.spongepowered.api.Sponge.getPlatform().getMinecraftVersion(); + let version = this.ServerType == 'bukkit' ? org.bukkit.Bukkit.getServer().getVersion() : org.spongepowered.api.Sponge.getPlatform().getMinecraftVersion() this.sendResult(ctx, "type", this.ServerType) - return `Currect Server Version is ${version}`; + return `Currect Server Version is ${version}` } } @@ -96,62 +113,63 @@ export class WebSocket extends interfaces.Plugin { } } -const ChannelInboundHandlerAdapter = Java.type('io.netty.channel.ChannelInboundHandlerAdapter'); +const ChannelInboundHandlerAdapter = Java.type('io.netty.channel.ChannelInboundHandlerAdapter') const CharsetUtil = Java.type('io.netty.util.CharsetUtil') const TextWebSocketFrame = Java.type('io.netty.handler.codec.http.websocketx.TextWebSocketFrame') const MiaoDetectHandler = Java.extend(ChannelInboundHandlerAdapter, { - channelRead: function(ctx: any, channel: any) { + channelRead: (ctx: any, channel: any) => { channel.pipeline().addFirst('miaowebsocket', new WebSocketHandler()) - ctx.fireChannelRead(channel); + ctx.fireChannelRead(channel) } }) const TypeParameterMatcher = Java.type('io.netty.util.internal.TypeParameterMatcher') -const DefaultHttpResponse = Java.type('io.netty.handler.codec.http.DefaultHttpResponse'); -const DefaultFullHttpResponse = Java.type('io.netty.handler.codec.http.DefaultFullHttpResponse'); -const HttpHeaders = Java.type('io.netty.handler.codec.http.HttpHeaders'); -const HttpVersion = Java.type('io.netty.handler.codec.http.HttpVersion'); -const HttpResponseStatus = Java.type('io.netty.handler.codec.http.HttpResponseStatus'); -const LastHttpContent = Java.type('io.netty.handler.codec.http.LastHttpContent'); -const HttpServerCodec = Java.type('io.netty.handler.codec.http.HttpServerCodec'); -const ChunkedWriteHandler = Java.type('io.netty.handler.stream.ChunkedWriteHandler'); -const HttpObjectAggregator = Java.type('io.netty.handler.codec.http.HttpObjectAggregator'); -const WebSocketServerProtocolHandler = Java.type('io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler'); -const SimpleChannelInboundHandler = Java.type('io.netty.channel.SimpleChannelInboundHandler'); +const DefaultHttpResponse = Java.type('io.netty.handler.codec.http.DefaultHttpResponse') +const DefaultFullHttpResponse = Java.type('io.netty.handler.codec.http.DefaultFullHttpResponse') +const HttpHeaders = Java.type('io.netty.handler.codec.http.HttpHeaders') +const HttpVersion = Java.type('io.netty.handler.codec.http.HttpVersion') +const HttpResponseStatus = Java.type('io.netty.handler.codec.http.HttpResponseStatus') +const LastHttpContent = Java.type('io.netty.handler.codec.http.LastHttpContent') +const HttpServerCodec = Java.type('io.netty.handler.codec.http.HttpServerCodec') +const ChunkedWriteHandler = Java.type('io.netty.handler.stream.ChunkedWriteHandler') +const HttpObjectAggregator = Java.type('io.netty.handler.codec.http.HttpObjectAggregator') +const WebSocketServerProtocolHandler = Java.type('io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler') +const SimpleChannelInboundHandler = Java.type('io.netty.channel.SimpleChannelInboundHandler') const FullHttpRequestMatcher = TypeParameterMatcher.get(base.getClass('io.netty.handler.codec.http.FullHttpRequest')) -const File = Java.type('java.io.File'); -const RandomAccessFile = Java.type('java.io.RandomAccessFile'); -const DefaultFileRegion = Java.type('io.netty.channel.DefaultFileRegion'); -const ChannelFutureListener = Java.type('io.netty.channel.ChannelFutureListener'); +const File = Java.type('java.io.File') +const RandomAccessFile = Java.type('java.io.RandomAccessFile') +const DefaultFileRegion = Java.type('io.netty.channel.DefaultFileRegion') +const ChannelFutureListener = Java.type('io.netty.channel.ChannelFutureListener') const HttpRequestHandler = Java.extend(SimpleChannelInboundHandler, { acceptInboundMessage: (msg: any) => { - return FullHttpRequestMatcher.match(msg); + return FullHttpRequestMatcher.match(msg) }, channelRead0: (ctx: any, request: any) => { if ('/ws' == request.getUri()) { ctx.fireChannelRead(request.retain()) } else { if (HttpHeaders.is100ContinueExpected(request)) { - ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); + ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)) } - let file = new File('/home/project/TSWorkSpace/ms/packages/plugins/public', request.getUri().split('?')[0]) - if (!file.exists()) { - ctx.write(new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND)); - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE); - return; + let filename = request.getUri().split('?')[0].substr(1) + let file = new File('/home/project/TSWorkSpace/ms/packages/plugins/public', filename || 'index.html') + if (!file.exists() || !file.isFile()) { + ctx.write(new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND)) + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE) + return } let response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK) - response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); - let raf = new RandomAccessFile(file, 'r'); - let keepAlive = HttpHeaders.isKeepAlive(request); + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html charset=UTF-8") + let raf = new RandomAccessFile(file, 'r') + let keepAlive = HttpHeaders.isKeepAlive(request) if (keepAlive) { - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()); - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()) + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE) } - ctx.write(response); - ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length())); - let future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); + ctx.write(response) + ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length())) + let future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT) if (!keepAlive) { - future.addListener(ChannelFutureListener.CLOSE); + future.addListener(ChannelFutureListener.CLOSE) } } } @@ -160,38 +178,37 @@ const TextWebSocketFrameMatcher = TypeParameterMatcher.get(base.getClass('io.net const TextWebSocketFrameHandler = Java.extend(SimpleChannelInboundHandler, { userEventTriggered: (ctx: any, evt: any) => { if (evt == 'HANDSHAKE_COMPLETE') { - clients.push(ctx.channel()); + clients.push(ctx.channel()) console.console(`§6[§cMS§6][§bWebSocket§6]§r new client §b${ctx.channel().id()} §aconnected...`) } }, acceptInboundMessage: (msg: any) => { - return TextWebSocketFrameMatcher.match(msg); + return TextWebSocketFrameMatcher.match(msg) }, channelRead0: (ctx: any, msg: any) => { - container.get('onmessage')(ctx, msg); + container.get('onmessage')(ctx, msg) } }) const WebSocketHandler = Java.extend(ChannelInboundHandlerAdapter, { channelRead: function(ctx: any, msg: any) { - msg.markReaderIndex(); - let message: string = msg.toString(CharsetUtil.UTF_8); - let channel = ctx.channel(); - let pipeline = channel.pipeline(); + msg.markReaderIndex() + let message: string = msg.toString(CharsetUtil.UTF_8) + let channel = ctx.channel() + let pipeline = channel.pipeline() if (message.indexOf('HTTP/1.1') > 0) { - try { - 'protocol_lib_finish protocol_lib_decoder protocol_lib_encoder'.split(' ').forEach(f => channel.pipeline().remove(f)) - } catch (error) { - } - 'timeout legacy_query splitter decoder prepender encoder packet_handler'.split(' ').forEach(f => channel.pipeline().remove(f)) - pipeline.addLast('http', new HttpServerCodec()); - pipeline.addLast('chunk', new ChunkedWriteHandler()); - pipeline.addLast('httpobj', new HttpObjectAggregator(64 * 1024)); - pipeline.addLast('http_request', new HttpRequestHandler()); - pipeline.addLast('websocket', new WebSocketServerProtocolHandler("/ws")); - pipeline.addLast('websocket_handler', new TextWebSocketFrameHandler()); + channel.pipeline().names().forEach(f => { + if (f == 'miaowebsocket' || f.indexOf('DefaultChannelPipeline') > -1) { return } + pipeline.remove(f) + }) + pipeline.addLast('http', new HttpServerCodec()) + pipeline.addLast('chunk', new ChunkedWriteHandler()) + pipeline.addLast('httpobj', new HttpObjectAggregator(64 * 1024)) + pipeline.addLast('http_request', new HttpRequestHandler()) + pipeline.addLast('websocket', new WebSocketServerProtocolHandler("/ws")) + pipeline.addLast('websocket_handler', new TextWebSocketFrameHandler()) } - pipeline.remove('miaowebsocket'); - msg.resetReaderIndex(); - ctx.fireChannelRead(msg); + pipeline.remove('miaowebsocket') + msg.resetReaderIndex() + ctx.fireChannelRead(msg) } })