Compare commits

...

72 Commits

Author SHA1 Message Date
e70c78a255 v0.28.0-beta.2 2023-08-08 18:11:44 +08:00
9cfac1672a feat: optimize websocket client 2023-08-08 18:06:34 +08:00
9126ec8035 v0.28.0-beta.1 2023-07-30 16:46:42 +08:00
23bc6068b5 feat: optimize websocket client 2023-07-30 16:46:34 +08:00
27b428fbe2 v0.28.0-beta.0 2023-07-26 17:37:26 +08:00
c07f1131c4 refactor: optimize plugin system 2023-07-26 17:35:33 +08:00
002f2c47c6 v0.27.6 2023-07-19 18:22:35 +08:00
8633d9ea95 fix: particle spawner error 2023-07-19 18:21:56 +08:00
07a5d0c8de feat: add plugin checker
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-18 16:01:15 +08:00
dd76e563c8 feat: add qrcode package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-18 15:21:42 +08:00
7b85ff5b7c feat: 同步 socket.io 上游代码
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-09 13:49:48 +08:00
359aeb9d63 chore: remove client package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-09 12:44:04 +08:00
3901d9fb5f feat: 更新依赖版本
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-08 17:03:17 +08:00
c2867da047 v0.26.0 2023-02-08 16:24:09 +08:00
65832c9fae feat: 完善粒子播放
Signed-off-by: MiaoWoo <admin@yumc.pw>
2023-02-08 16:23:05 +08:00
d9dffa704d v2.25.1 2022-11-28 17:30:45 +08:00
b39f29de6a v2.25.1-beta.1 2022-11-28 17:18:50 +08:00
56334c6f6e v0.25.1-beta.0 2022-11-28 17:15:39 +08:00
72673b2a67 feat: optimize event slow exec cache
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-28 17:14:29 +08:00
597bdb721a v0.25.0 2022-11-22 17:37:54 +08:00
6e0456d777 feat: 添加注释
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-22 17:35:13 +08:00
fb67b06230 v0.25.0-beta.0 2022-11-22 17:12:22 +08:00
203560dcf2 chore: add --verify-access
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-22 17:10:15 +08:00
3f58f5992c style: xml-http-request.ts
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-22 15:50:54 +08:00
ac16754c9c fix: buffer polyfill error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-22 13:48:41 +08:00
42d637dd63 fix: rename Buffer to BufferPolyfill
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-21 23:27:12 +08:00
df0d246136 feat: upgrade socket.io to v4
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-21 23:18:39 +08:00
e563e1b507 feat: update chat to 1.19.2
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-21 23:17:14 +08:00
3b822c613a dep: update dependencies
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-11-21 14:29:17 +08:00
2967c2a1fe v0.24.3 2022-10-14 14:12:02 +08:00
1c579c9789 feat: framework optimization
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-10-14 10:07:36 +08:00
2fe9bce2ea v0.24.2 2022-07-17 07:13:54 +08:00
496d278a93 fix: item fromJson error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-07-17 07:11:36 +08:00
bdf674b678 v0.24.1 2022-07-07 00:54:16 +08:00
67fe13deac fix: migrate config error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-07-07 00:51:10 +08:00
30846cdc87 v0.24.0 2022-07-06 22:01:09 +08:00
d8d03149df fix: some bug
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-07-06 21:57:36 +08:00
b6d7847a79 v0.23.0 2022-06-20 00:59:07 +08:00
58f59f8260 v0.23.0-beta.0 2022-06-20 00:57:37 +08:00
f3fa14990f v0.23.0-alpha.0 2022-06-20 00:52:40 +08:00
15d1f8392b release: v0.23.0
1. add item api
2. support rollup source map
3. fix database drvice error
4. support loliserver
5. support 1.19 bukkit chat
6. config add migrate options
7.

Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-06-20 00:48:00 +08:00
b5fac23c5c v0.22.0 2022-05-21 15:05:14 +08:00
148f6c28c4 feat enable faster load mode
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-21 11:54:40 +08:00
880065495e feat: add database web manager
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-21 11:54:09 +08:00
7fc70a92d5 v0.21.2 2022-05-14 16:44:25 +08:00
df6da12a8a feat: modify package description
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-14 16:43:50 +08:00
082f2b8f73 v0.21.0 2022-05-14 16:28:17 +08:00
7cd85f3f34 feat: add verify package signle
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-14 11:55:05 +08:00
073cf6aaac Revert "feat: lerna link convert"
This reverts commit 4ee8fc9a20.
2022-05-14 10:45:06 +08:00
43aa39b86f feat: remove verify package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-14 09:54:15 +08:00
cb58fbdb6c feat: add verify package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-13 23:50:02 +08:00
86aacf1a1f feat: optimize ioc loader
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-11 10:56:47 +08:00
47413c6766 feat: refactor database
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-05-11 10:54:44 +08:00
4ee8fc9a20 feat: lerna link convert
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-26 07:52:43 +08:00
8d0484eefb feat: add loadMavenDepend & optimize database
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-25 00:41:11 +08:00
579d89ae89 feat: update dependencies
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-21 07:25:55 +08:00
010f561766 v0.20.0 2022-04-19 10:04:56 +08:00
848aacd991 feat: add beta publish script
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-19 09:56:37 +08:00
ab5559b26f v0.20.0-alpha.0 2022-04-19 09:40:08 +08:00
46729b9cf0 feat: optimize config loader
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-19 09:29:57 +08:00
83cad2f52e v0.19.8 2022-04-04 05:17:27 +08:00
a5cb084767 feat: remove global initialize
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-04 05:13:24 +08:00
16ab108186 v0.19.7 2022-04-04 04:28:12 +08:00
a1df719dc9 fix: 修复子命令权限处理异常
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-04 04:27:47 +08:00
41e5754492 v0.19.6 2022-04-03 03:10:13 +08:00
866d1dd62c fix: api interface error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-03 03:07:18 +08:00
8056d3060d v0.19.5 2022-04-03 02:43:11 +08:00
b8440b83de feat: add exit event
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-03 02:41:17 +08:00
b55e662f81 feat: add more event emit
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-02 16:51:08 +08:00
db526e9444 fix: 修复自动化命令参数错误
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-02 15:02:21 +08:00
1b0ea9fa05 v0.19.0 2022-04-01 17:35:48 +08:00
143c960da5 feat: new mjs scanner
Signed-off-by: MiaoWoo <admin@yumc.pw>
2022-04-01 17:18:32 +08:00
159 changed files with 16862 additions and 6593 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.yarn*
.vscode .vscode
.theia .theia
node_modules node_modules

View File

@@ -1,3 +1,4 @@
src
test test
typings typings
bundled bundled

View File

@@ -8,27 +8,28 @@
```txt ```txt
└─packages └─packages
├─api 全平台兼容的接口 ├─api 全平台兼容的接口
├─core 核心代码 用于引导加载 ├─core 核心代码 用于引导加载
├─common 公共类库代码 例如 http reflect 模块 ├─common 公共类库代码 例如 http reflect 模块
├─client NodeJS的Minecraft客户端 用于调试插件 ├─compile 编译器相关功能
├─container IOC容器 用于注入具体实现 ├─client NodeJS 的 Minecraft 客户端 已迁移至 ms-client
├─ployfill Nashorn 的一些自定义增强 ├─container IOC容器 用于注入具体实现
├─nashorn Nashorn 的类型定义 ├─database 数据库相关功能
├─bungee BungeeCordAPI内部实现 ├─protocol 协议处理相关功能
├─bukkit BukkitAPI内部实现 ├─service 服务相关功能
├─sponge SpongeAPI内部实现 ├─i18n 多语言环境相关支持
├─nukkit NukkitAPI内部实现 ├─polyfill Nashorn 的一些自定义增强
├─plugin 插件管理器 ├─nashorn Nashorn 的类型定义
├─websocket Netty的WebSocket注入 ├─nodejs NodeJS 的部分 Java 实现
├─type Java的类型定义 ├─bungee BungeeCordAPI 内部实现
| ├─bungee BungeeCord类型定义 ├─bukkit BukkitAPI 内部实现
| ├─bukkit Bukkit类型定义 ├─sponge SpongeAPI 内部实现
| ├─sponge Sponge类型定义 ├─nukkit NukkitAPI 内部实现
| └─nukkit Nukkit类型定义 ├─molang MoLang 解析库
└─plugins 这里当然是插件啦 ├─qrcode 二维码相关类库
├─bungee 只兼容BungeeCord的插件 ├─plugin 插件管理器
├─bukkit 只兼容Bukkit的插件 ├─websocket WebSocket 相关实现
├─sponge 只兼容Sponge的插件 | ├─client 基于 Netty 的 WebSocket 客户端
└─nukkit 只兼容Nukkit的插件 | └─server 基于 Netty 的 WebSocket 服务端
└─type 类型定义 已迁移到 @javatypes
``` ```

View File

@@ -1,17 +1,15 @@
{ {
"version": "0.18.0", "version": "0.28.0-beta.2",
"useWorkspaces": true, "npmClient": "yarn",
"npmClient": "yarn", "packages": [
"packages": [ "packages/*"
"packages/*" ],
], "command": {
"command": { "run": {
"run": { "stream": true
"stream": true
},
"publish": {
"access": "public",
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
}
} }
},
"publishConfig": {
"access": "public"
}
} }

View File

@@ -10,15 +10,19 @@
"clean": "lerna run clean", "clean": "lerna run clean",
"watch": "lerna run watch --parallel", "watch": "lerna run watch --parallel",
"build": "lerna run build", "build": "lerna run build",
"ug": "yarn upgrade-interactive --latest", "ug": "yarn upgrade-interactive",
"np": "./script/push.sh", "np": "./script/push.sh",
"lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms", "lsp": "npm login -scope=@ccms",
"lp": "lerna publish --registry https://registry.npmjs.org" "lp": "lerna publish --force-publish",
"lpb": "lerna publish --preid beta --dist-tag beta --force-publish",
"lpc": "lerna publish --canary --preid beta --pre-dist-tag beta --force-publish",
"lpf": "lerna publish from-package --yes",
"sync": "./script/sync.sh"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"devDependencies": { "devDependencies": {
"lerna": "^4.0.0" "lerna": "^7.1.4"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/amqp", "name": "@ccms/amqp",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript amqp package", "description": "MiaoScript amqp package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,17 +19,17 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"@javatypes/amqp-client": "^0.0.3", "@javatypes/amqp-client": "^0.0.3",
"@javatypes/spring-amqp": "^0.0.3", "@javatypes/spring-amqp": "^0.0.3",
"@javatypes/spring-rabbit": "^0.0.3", "@javatypes/spring-rabbit": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/api", "name": "@ccms/api",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript api package", "description": "MiaoScript api package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,16 +19,16 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0", "@ccms/container": "^0.28.0-beta.2",
"@ccms/polyfill": "^0.18.0", "@ccms/polyfill": "^0.28.0-beta.2",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"source-map-builder": "^0.0.7" "source-map-builder": "^0.0.7"
}, },
"devDependencies": { "devDependencies": {
"@types/base64-js": "^1.3.0", "@types/base64-js": "^1.3.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -5,6 +5,13 @@ import { plugin } from './plugin'
export namespace command { export namespace command {
@injectable() @injectable()
export abstract class Command { export abstract class Command {
/**
* first time script engine need optimize jit code
* so ignore first slow exec notify
*/
private cacheSlowCommandKey = {};
private cacheSlowCompleteKey = {};
/** /**
* 注册插件命令 * 注册插件命令
* @param plugin 插件 * @param plugin 插件
@@ -44,12 +51,33 @@ export namespace command {
protected setExecutor(plugin: plugin.Plugin, command: any, executor: Function) { protected setExecutor(plugin: plugin.Plugin, command: any, executor: Function) {
return (sender: any, _: any, command: string, args: string[]) => { return (sender: any, _: any, command: string, args: string[]) => {
try { try {
return executor(sender, command, Java.from(args)) let time = Date.now()
let result = executor(sender, command, Java.from(args))
let cost = Date.now() - time
if (cost > global.ScriptSlowExecuteTime) {
let commandKey = `${plugin.description.name}-${command}-${sender.name}`
if (!this.cacheSlowCommandKey[commandKey]) { return this.cacheSlowCommandKey[commandKey] = cost }
console.i18n("ms.api.command.execute.slow", {
player: sender.name,
plugin: plugin.description.name,
command,
args: Java.from(args).join(' '),
cost
})
}
return result
} catch (ex: any) { } catch (ex: any) {
console.i18n("ms.api.command.execute.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }) let message = i18n.translate("ms.api.command.execute.error", {
player: sender.name,
plugin: plugin.description.name,
command,
args: Java.from(args).join(' '),
ex
})
console.console(message)
console.ex(ex) console.ex(ex)
if (sender.name != 'CONSOLE') { if (sender.name != 'CONSOLE') {
console.sender(sender, [i18n.translate("ms.api.command.execute.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }), ...console.stack(ex)]) console.sender(sender, [message, ...console.stack(ex)])
} }
return true return true
} }
@@ -58,13 +86,36 @@ export namespace command {
protected setTabCompleter(plugin: plugin.Plugin, command: any, tabCompleter: Function) { protected setTabCompleter(plugin: plugin.Plugin, command: any, tabCompleter: Function) {
return (sender: any, _: any, command: string, args: string[]) => { return (sender: any, _: any, command: string, args: string[]) => {
try { try {
let time = Date.now()
var token = args[args.length - 1] var token = args[args.length - 1]
var complete = tabCompleter(sender, command, Java.from(args)) || [] var complete = tabCompleter(sender, command, Java.from(args)) || []
return this.copyPartialMatches(complete, token) let result = this.copyPartialMatches(complete, token)
let cost = Date.now() - time
if (cost > global.ScriptSlowExecuteTime) {
let completerKey = `${plugin.description.name}-${command}-${sender.name}`
if (!this.cacheSlowCompleteKey[completerKey]) { return this.cacheSlowCompleteKey[completerKey] = cost }
console.i18n("ms.api.command.tab.completer.slow", {
player: sender.name,
plugin: plugin.description.name,
command,
args: Java.from(args).join(' '),
cost
})
}
return result
} catch (ex: any) { } catch (ex: any) {
console.i18n("ms.api.command.tab.completer.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }) let message = i18n.translate("ms.api.command.tab.completer.error", {
player: sender.name,
plugin: plugin.description.name,
command,
args: Java.from(args).join(' '),
ex
})
console.console(message)
console.ex(ex) console.ex(ex)
console.sender(sender, [i18n.translate("ms.api.command.tab.completer.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }), ...console.stack(ex)]) if (sender.name != 'CONSOLE') {
console.sender(sender, [message, ...console.stack(ex)])
}
return [] return []
} }
} }

View File

@@ -5,7 +5,7 @@ import * as base64 from 'base64-js'
const Arrays = Java.type('java.util.Arrays') const Arrays = Java.type('java.util.Arrays')
const Level = Java.type('java.util.logging.Level') const Level = Java.type('java.util.logging.Level')
const Paths = Java.type('java.nio.file.Paths') const Paths = Java.type('java.nio.file.Paths')
const ignoreLogPrefix = ['java.', 'javax.', 'sun.', 'net.minecraft.', 'org.bukkit.', 'jdk.nashorn.', 'org.openjdk.nashorn', 'io.netty.', 'org.spongepowered.', 'org.apache', 'org.springframework'] const ignoreLogPrefix = ['java.', 'javax.', 'sun.', 'net.minecraft.', 'org.bukkit.', 'jdk.internal.dynalink.', 'jdk.nashorn.', 'org.openjdk.nashorn', 'io.netty.', 'org.spongepowered.', 'org.apache', 'org.springframework']
enum LogLevel { enum LogLevel {
ALL, ALL,
@@ -18,12 +18,114 @@ enum LogLevel {
OFF OFF
} }
const sourceMaps: { [key: string]: SourceMapBuilder } = {}
const sourceFileMaps: { [key: string]: string } = {}
global.setGlobal('MiaoScriptSourceMaps', sourceMaps)
global.setGlobal('MiaoScriptSourceFileMaps', sourceFileMaps)
export namespace jsconsole {
export function readSourceMap(fileName: string, lineNumber: any) {
try {
if (fileName.endsWith('js') || fileName.endsWith('ms')) {
if (sourceMaps[fileName] === undefined) {
sourceMaps[fileName] = null
let sourceLine = base.read(fileName).split('\n')
let lastLine = sourceLine[sourceLine.length - 1] || sourceLine[sourceLine.length - 2]
// lastLine is similar //# sourceMappingURL=data:application/json;base64,
if (lastLine.startsWith('//# sourceMappingURL=')) {
let sourceContent = null
let sourceMappingURL = lastLine.split('sourceMappingURL=', 2)[1]
if (sourceMappingURL.startsWith('data:application/json;base64,')) {
sourceContent = String.fromCharCode(...Array.from(base64.toByteArray(sourceMappingURL.split(',', 2)[1])))
} else if (sourceMappingURL.startsWith('http://') || sourceMappingURL.startsWith('https://')) {
// TODO
} else {
let file = Paths.get(Paths.get(fileName, '..', sourceMappingURL).toFile().getCanonicalPath()).toFile()
if (file.exists()) {
sourceContent = base.read(file)
sourceFileMaps[fileName] = file.getCanonicalPath()
} else if (global.debug) {
console.debug('readSourceMap can\'t found', fileName, 'source map file', sourceMappingURL)
}
}
if (sourceContent) {
sourceMaps[fileName] = new SourceMapBuilder(JSON.parse(sourceContent))
}
}
}
if (sourceMaps[fileName]) {
let sourceMapping = sourceMaps[fileName].getSource(lineNumber, 25, true, true)
if (sourceMapping) {
fileName = Paths.get(fileName, '..', sourceMapping.sourcePath).toFile().getCanonicalPath()
lineNumber = sourceMapping.mapping.sourceLine
}
}
}
} catch (error: any) {
console.debug('search source map', fileName, 'line', lineNumber, 'error:', error)
if (global.debug) {
console.ex(error)
}
}
return {
fileName,
lineNumber
}
}
export function getStackTrace(ex: Error, color: boolean = true): string[] {
if (!ex) return []
let stack = ex.getStackTrace()
let cache = [(color ? '§c' : '') + ex]
//@ts-ignore
if (stack.class) {
stack = Arrays.asList(stack)
}
stack.forEach(trace => {
if (!trace.fileName || trace.fileName.startsWith('jar:file:') || trace.fileName.startsWith('file:')) { return }
if (trace.className.startsWith('<')) {
let { fileName, lineNumber } = readSourceMap(trace.fileName, trace.lineNumber)
if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] }
if (color) {
cache.push(` §e->§c ${fileName}:${lineNumber}(${trace.lineNumber}) => §4${trace.methodName}`)
} else {
cache.push(` -> ${fileName}:${lineNumber}(${trace.lineNumber}) => ${trace.methodName}`)
}
} else {
let className = trace.className
var fileName = trace.fileName as string
var lineNumber = trace.lineNumber
if (className.startsWith('jdk.nashorn.internal.scripts') || className.startsWith('org.openjdk.nashorn.internal.scripts')) {
className = className.substr(className.lastIndexOf('$') + 1)
var { fileName, lineNumber } = readSourceMap(fileName, lineNumber)
if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] }
} else {
if (!global.debug) {
for (let prefix in ignoreLogPrefix) {
if (className.startsWith(ignoreLogPrefix[prefix])) {
return
}
}
} else if (className.startsWith('jdk.nashorn.internal.') || className.startsWith('org.openjdk.nashorn.internal.')) {
return
}
}
if (color) {
cache.push(` §e->§c ${className}.${trace.methodName}(§4${fileName}:${lineNumber}§c)`)
} else {
cache.push(` -> ${className}.${trace.methodName}(${fileName}:${lineNumber})`)
}
}
})
return cache
}
}
export class MiaoScriptConsole implements Console { export class MiaoScriptConsole implements Console {
Console: any Console: any
memory: any memory: any
private static sourceMaps: { [key: string]: SourceMapBuilder } = {}
private static sourceFileMaps: { [key: string]: string } = {}
private _name: string = '' private _name: string = ''
private _level: LogLevel = LogLevel.INFO private _level: LogLevel = LogLevel.INFO
@@ -92,91 +194,8 @@ export class MiaoScriptConsole implements Console {
ex(ex: Error) { ex(ex: Error) {
this.stack(ex).forEach(line => this.console(line)) this.stack(ex).forEach(line => this.console(line))
} }
readSourceMap(fileName: string, lineNumber: any) {
try {
if (fileName.endsWith('js')) {
if (MiaoScriptConsole.sourceMaps[fileName] === undefined) {
MiaoScriptConsole.sourceMaps[fileName] = null
let sourceLine = base.read(fileName).split('\n')
let lastLine = sourceLine[sourceLine.length - 1]
// lastLine is similar //# sourceMappingURL=data:application/json;base64,
if (lastLine.startsWith('//# sourceMappingURL=')) {
let sourceContent = null
let sourceMappingURL = lastLine.split('sourceMappingURL=', 2)[1]
if (sourceMappingURL.startsWith('data:application/json;base64,')) {
sourceContent = String.fromCharCode(...Array.from(base64.toByteArray(sourceMappingURL.split(',', 2)[1])))
} else if (sourceMappingURL.startsWith('http://') || sourceMappingURL.startsWith('https://')) {
// TODO
} else {
let file = Paths.get(Paths.get(fileName, '..', sourceMappingURL).toFile().getCanonicalPath()).toFile()
if (file.exists()) { sourceContent = base.read(file) }
}
if (sourceContent) {
MiaoScriptConsole.sourceMaps[fileName] = new SourceMapBuilder(JSON.parse(sourceContent))
MiaoScriptConsole.sourceFileMaps[fileName] = Paths.get(fileName, '..', MiaoScriptConsole.sourceMaps[fileName].sources[0]).toFile().getCanonicalPath()
}
}
}
if (MiaoScriptConsole.sourceMaps[fileName]) {
let sourceMapping = MiaoScriptConsole.sourceMaps[fileName].getSource(lineNumber, 25, true, true)
fileName = MiaoScriptConsole.sourceFileMaps[fileName]
if (sourceMapping && lineNumber != sourceMapping.mapping.sourceLine) { lineNumber = sourceMapping.mapping.sourceLine }
}
}
} catch (error: any) {
console.debug('search source map', fileName, 'line', lineNumber, 'error:', error)
if (global.debug) {
console.ex(error)
}
}
return {
fileName,
lineNumber
}
}
stack(ex: Error, color: boolean = true): string[] { stack(ex: Error, color: boolean = true): string[] {
if (!ex) return [] return jsconsole.getStackTrace(ex, color)
let stack = ex.getStackTrace()
let cache = [(color ? '§c' : '') + ex]
//@ts-ignore
if (stack.class) {
stack = Arrays.asList(stack)
}
stack.forEach(trace => {
if (!trace.fileName || trace.fileName.startsWith('jar:file:') || trace.fileName.startsWith('file:')) { return }
if (trace.className.startsWith('<')) {
let { fileName, lineNumber } = this.readSourceMap(trace.fileName, trace.lineNumber)
if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] }
if (color) {
cache.push(` §e->§c ${fileName}:${lineNumber} => §4${trace.methodName}`)
} else {
cache.push(` -> ${fileName}:${lineNumber} => ${trace.methodName}`)
}
} else {
let className = trace.className
var fileName = trace.fileName as string
var lineNumber = trace.lineNumber
if (className.startsWith('jdk.nashorn.internal.scripts') || className.startsWith('org.openjdk.nashorn.internal.scripts')) {
className = className.substr(className.lastIndexOf('$') + 1)
var { fileName, lineNumber } = this.readSourceMap(fileName, lineNumber)
if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] }
} else {
if (!global.debug) {
for (let prefix in ignoreLogPrefix) {
if (className.startsWith(ignoreLogPrefix[prefix])) {
return
}
}
}
}
if (color) {
cache.push(` §e->§c ${className}.${trace.methodName}(§4${fileName}:${lineNumber}§c)`)
} else {
cache.push(` -> ${className}.${trace.methodName}(${fileName}:${lineNumber})`)
}
}
})
return cache
} }
assert(value: any, message?: string, ...optionalParams: any[]): void { assert(value: any, message?: string, ...optionalParams: any[]): void {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")

View File

@@ -11,10 +11,21 @@ export namespace constants {
} }
export namespace Reflect { export namespace Reflect {
export const Method = { export const Method = {
getServerConnection: [/*spigot 1.8.8*/'aq',/*spigot 1.12.2*/ 'an', /*spigot 1.14.4+*/'getServerConnection', /*catserver 1.12.2*/'func_147137_ag'] getServerConnection: [
/*spigot 1.8.8*/'aq',
/*spigot 1.12.2*/ 'an',
/*spigot 1.14.4+*/'getServerConnection',
/*spigot 1.19+*/'ad',
/*catserver 1.12.2*/'func_147137_ag'
]
} }
export const Field = { export const Field = {
listeningChannels: [/*spigot 1.8.8-1.12.2*/'g', /*spigot 1.14.4*/'f', /*spigot 1.15.2+*/'listeningChannels', /*catserver 1.12.2*/'field_151274_e'] listeningChannels: [
/*spigot 1.8.8-1.12.2*/'g',
/*spigot 1.14.4*/'f',
/*spigot 1.15.2+*/'listeningChannels',
/*catserver 1.12.2*/'field_151274_e'
]
} }
} }
export enum ServerType { export enum ServerType {

View File

@@ -1,5 +1,56 @@
import { injectable } from "@ccms/container"
export namespace database { export namespace database {
export const DataBaseManager = Symbol("DataBaseManager"); export const DataSource = Symbol("DataSource")
export const DataSource = Symbol("DataSource"); /**
export const DataBase = Symbol("DataBase"); * 数据库配置
*/
export interface DataBaseConfig {
/**
* 数据库类型
*/
type: 'h2' | 'mysql' | 'mongodb' | 'sqlite' | 'postgres' | 'redis'
/**
* 数据库连接串
*/
url: string | javax.sql.DataSource
/**
* 数据库驱动
*/
driverClassName?: string
/**
* 用户名
*/
username?: string
/**
* 密码
*/
password?: string
/**
* 链接属性
*/
properties?: { [key: string]: any }
/**
* 调试模式
*/
debug?: boolean
}
@injectable()
export abstract class DataBaseManager {
abstract setMainDatabase(mainDatabase: DataBase): void
abstract getMainDatabase(): DataBase
abstract createDatabase(name: string, config: DataBaseConfig): DataBase
abstract removeDatabase(name: string): boolean
abstract getDatabase(name: string): DataBase
abstract startWebManager(...args: string[])
abstract stopWebManager()
abstract shutdown()
}
@injectable()
export abstract class DataBase {
abstract query<T>(sql: string, ...args: any[]): Array<T>
abstract update(sql: string, ...args: any[]): number
abstract execute(sql: string): void
}
} }

View File

@@ -25,7 +25,8 @@ export namespace event {
public EventPriority = EventPriority; public EventPriority = EventPriority;
private mapEvent = []; private mapEvent = [];
private listenerMap = []; private pluginEventMap = [];
private cacheSlowEventKey = {};
protected baseEventDir = ''; protected baseEventDir = '';
@@ -92,17 +93,23 @@ export namespace event {
return eventCls return eventCls
} }
execute(name, exec, eventCls) { /**
* 创建命令执行器
* @param name 插件名称
* @param exec 执行方法
* @param eventCls 事件类
* @returns
*/
createExecute(name, exec, eventCls) {
return (...args: any[]) => { return (...args: any[]) => {
let event = args[args.length - 1]
try { try {
let event = args[args.length - 1] if (!eventCls.isAssignableFrom(event.getClass())) { return }
if (eventCls.isAssignableFrom(event.getClass())) { let time = Date.now(); exec(event); let cost = Date.now() - time
let time = Date.now() if (cost > global.ScriptSlowExecuteTime && !event.async) {
exec(event) let eventKey = `${name}-${this.class2Name(eventCls)}`
let cost = Date.now() - time if (!this.cacheSlowEventKey[eventKey]) { return this.cacheSlowEventKey[eventKey] = cost }
if (cost > 20) { console.i18n("ms.api.event.execute.slow", { name, event: this.class2Name(eventCls), cost })
console.i18n("ms.api.event.execute.slow", { name, event: this.class2Name(eventCls), cost })
}
} }
} catch (ex: any) { } catch (ex: any) {
console.i18n("ms.api.event.execute.error", { name, event: this.class2Name(eventCls), ex }) console.i18n("ms.api.event.execute.error", { name, event: this.class2Name(eventCls), ex })
@@ -113,17 +120,17 @@ export namespace event {
/** /**
* 添加事件监听 * 添加事件监听
* @param plugin {any} * @param plugin {any} 插件
* @param event {string} * @param event {string} 事件名称
* @param exec {function} * @param exec {function} 事件执行器
* @param priority {string} [LOWEST,LOW,NORMAL,HIGH,HIGHEST,MONITOR] * @param priority {string} [LOWEST,LOW,NORMAL,HIGH,HIGHEST,MONITOR] 优先级
* @param ignoreCancel * @param ignoreCancel 是否忽略已取消事件
*/ */
listen(plugin: any, event: string, exec: (event: any) => void, priority: EventPriority = EventPriority.NORMAL, ignoreCancel = false) { listen(plugin: any, event: string, exec: (event: any) => void, priority: EventPriority = EventPriority.NORMAL, ignoreCancel = false) {
if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError(i18n.translate("ms.api.event.listen.plugin.name.empty")) if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError(i18n.translate("ms.api.event.listen.plugin.name.empty"))
var name = plugin.description.name var name = plugin.description.name
var eventCls = this.name2Class(name, event) var eventCls = this.name2Class(name, event)
if (!eventCls) { return } if (!eventCls) { return () => { console.warn('event ' + event + ' not found ignore off listener.') } }
if (typeof priority === 'boolean') { if (typeof priority === 'boolean') {
ignoreCancel = priority ignoreCancel = priority
priority = EventPriority.NORMAL priority = EventPriority.NORMAL
@@ -133,19 +140,33 @@ export namespace event {
// @ts-ignore // @ts-ignore
let executor = exec.name || exec.executor || '[anonymous]' let executor = exec.name || exec.executor || '[anonymous]'
// noinspection JSUnusedGlobalSymbols // noinspection JSUnusedGlobalSymbols
var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel) let listener = this.register(
var listenerMap = this.listenerMap eventCls,
this.createExecute(name, exec, eventCls),
priority,
ignoreCancel
)
// add to cache Be used for close plugin to close event // add to cache Be used for close plugin to close event
if (!listenerMap[name]) listenerMap[name] = [] if (!this.pluginEventMap[name]) this.pluginEventMap[name] = []
var off = () => { let off = () => {
if (off['offed']) return if (off['offed']) return
off['offed'] = true off['offed'] = true
this.unregister(eventCls, listener) this.unregister(eventCls, listener)
console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: executor })) console.debug(i18n.translate("ms.api.event.unregister", {
name,
event: this.class2Name(eventCls),
exec: executor
}))
} }
listenerMap[name].push(off) this.pluginEventMap[name].push(off)
// noinspection JSUnresolvedVariable // noinspection JSUnresolvedVariable
console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls), exec: executor })) console.debug(i18n.translate("ms.api.event.register", {
name,
event: this.class2Name(eventCls),
exec: executor,
priority,
ignore: ignoreCancel
}))
return off return off
} }
@@ -154,10 +175,10 @@ export namespace event {
* @param plugin 插件 * @param plugin 插件
*/ */
disable(plugin: any) { disable(plugin: any) {
var eventCache = this.listenerMap[plugin.description.name] var eventCache = this.pluginEventMap[plugin.description.name]
if (eventCache) { if (eventCache) {
eventCache.forEach(off => off()) eventCache.forEach((off: () => any) => off())
delete this.listenerMap[plugin.description.name] delete this.pluginEventMap[plugin.description.name]
} }
} }

View File

@@ -4,11 +4,13 @@ export * from './web'
export * from './amqp' export * from './amqp'
export * from './chat' export * from './chat'
export * from './task' export * from './task'
export * from './item'
export * from './event' export * from './event'
export * from './proxy' export * from './proxy'
export * from './plugin' export * from './plugin'
export * from './server' export * from './server'
export * from './console' export * from './console'
export { jsconsole as console } from './console'
export * from './channel' export * from './channel'
export * from './command' export * from './command'
export * from './database' export * from './database'

20
packages/api/src/item.ts Normal file
View File

@@ -0,0 +1,20 @@
import { injectable } from '@ccms/container'
export namespace item {
@injectable()
export abstract class Item {
abstract builder(): ItemBuilder
abstract toJson(item: any): string
abstract fromJson(json: string): any
}
export interface ItemBuilder {
from(item: any): ItemBuilder
create(type: string | number): ItemBuilder
name(name: string): ItemBuilder
lore(...lores: string[]): ItemBuilder
amount(amount: number): ItemBuilder
durability(durability: number): ItemBuilder
clone(): any
build(): any
}
}

View File

@@ -6,6 +6,12 @@ const UUID = Java.type('java.util.UUID')
const Math = Java.type('java.lang.Math') const Math = Java.type('java.lang.Math')
export namespace particle { export namespace particle {
@injectable()
export abstract class ParticleSpawner {
abstract spawn(location: any, particle: Particle)
abstract spawnToPlayer(player: any, location: any, particle: Particle)
}
/** /**
* 表示一个特效对象 * 表示一个特效对象
* *
@@ -23,6 +29,11 @@ export namespace particle {
private extra: number = 0; private extra: number = 0;
private data: Object = null; private data: Object = null;
/**
* Only Show To Player
*/
private player: any
constructor() { constructor() {
this.uuid = UUID.randomUUID().toString() this.uuid = UUID.randomUUID().toString()
} }
@@ -105,6 +116,15 @@ export namespace particle {
return this return this
} }
getPlayer() {
return this.player
}
setPlayer(player) {
this.player = player
return this
}
/** /**
* 通过给定一个坐标就可以使用已经指定的参数来播放粒子 * 通过给定一个坐标就可以使用已经指定的参数来播放粒子
* *
@@ -112,9 +132,14 @@ export namespace particle {
*/ */
spawn(location: any) { spawn(location: any) {
if (!this.spawner) throw new Error(`particle ${this.uuid} not set spawner can't spawn!`) if (!this.spawner) throw new Error(`particle ${this.uuid} not set spawner can't spawn!`)
this.spawner.spawn(location, this) if (this.player) {
this.spawner.spawnToPlayer(this.player, location, this)
} else {
this.spawner.spawn(location, this)
}
} }
} }
/** /**
* 表示一条线 * 表示一条线
* *
@@ -158,8 +183,7 @@ export namespace particle {
show() { show() {
for (let i = 0; i < this.length; i += this.step) { for (let i = 0; i < this.length; i += this.step) {
let vectorTemp = this.vector.clone().multiply(i) this.spawn(this.start.clone().add(this.vector.clone().multiply(i)))
this.spawn(this.start.clone().add(vectorTemp))
} }
} }
@@ -234,15 +258,6 @@ export namespace particle {
this.length = this.vector.length() this.length = this.vector.length()
this.vector.normalize() this.vector.normalize()
} }
public static buildLine(locA: any, locB: any, step: number, particle: any) {
let vectorAB = locB.clone().subtract(locA).toVector()
let vectorLength = vectorAB.length()
vectorAB.normalize()
for (let i = 0; i < vectorLength; i += step) {
ParticleManager.globalSpawner.spawn(locA.clone().add(vectorAB.clone().multiply(i)), particle)
}
}
} }
/** /**
* 表示一个弧 * 表示一个弧
@@ -331,9 +346,10 @@ export namespace particle {
@injectable() @injectable()
export abstract class ParticleManager { export abstract class ParticleManager {
public static globalSpawner: ParticleSpawner = undefined
@Autowired() @Autowired()
private taskManager: task.TaskManager private taskManager: task.TaskManager
@Autowired()
private particleSpawner: particle.ParticleSpawner
protected taskId: java.util.concurrent.atomic.AtomicInteger protected taskId: java.util.concurrent.atomic.AtomicInteger
protected cacheTasks = new Map<string, ParticleTask>() protected cacheTasks = new Map<string, ParticleTask>()
@@ -354,6 +370,10 @@ export namespace particle {
return this.taskManager return this.taskManager
} }
public getParticleSpawner() {
return this.particleSpawner
}
public create(particle: Particle, plugin?: plugin.Plugin) { public create(particle: Particle, plugin?: plugin.Plugin) {
let uuid = particle.getUUID() let uuid = particle.getUUID()
if (this.cacheTasks.has(uuid)) { if (this.cacheTasks.has(uuid)) {
@@ -389,14 +409,12 @@ export namespace particle {
} }
} }
protected create0(owner: plugin.Plugin, particle: Particle): ParticleTask { protected create0(owner: plugin.Plugin, particle: Particle): ParticleTask {
particle.setSpawner(this.getGlobalSpawner()) particle.setSpawner(this.getParticleSpawner())
return new ParticleTask(owner, particle, this) return new ParticleTask(owner, particle, this)
} }
protected abstract getGlobalSpawner(): ParticleSpawner
} }
export class ParticleTask { export class ParticleTask {
private particle: Particle private particle: Particle
private isAsync: boolean = false private isAsync: boolean = false
private interval: number = 0 private interval: number = 0
@@ -486,9 +504,4 @@ export namespace particle {
} }
} }
} }
export abstract class ParticleSpawner {
abstract spawnParticle(location: any, particle: any, count: number)
abstract spawn(location: any, particle: Particle)
}
} }

View File

@@ -20,7 +20,7 @@ export namespace plugin {
export abstract class PluginManager { export abstract class PluginManager {
abstract scan(folder: string): void abstract scan(folder: string): void
abstract build(): void abstract build(): void
abstract loadFromFile(file: string, scanner?: plugin.PluginScanner): Plugin abstract loadFromFile(file: string, ext?: any): Plugin
abstract load(...args: any[]): void abstract load(...args: any[]): void
abstract enable(...args: any[]): void abstract enable(...args: any[]): void
abstract disable(...args: any[]): void abstract disable(...args: any[]): void
@@ -149,7 +149,19 @@ export namespace plugin {
/** /**
* 插件名称 不填默认为类名 * 插件名称 不填默认为类名
*/ */
name?: string name: string
/**
* 插件中文名称
*/
cname?: string
/**
* 付费插件ID
*/
pid?: number
/**
* 付费插件等级 付费插件自动注入
*/
level?: number
/** /**
* 前缀 * 前缀
*/ */

View File

@@ -29,6 +29,7 @@ export namespace server {
origin: any origin: any
[key: string]: any [key: string]: any
} }
@injectable() @injectable()
export abstract class NativePluginManager { export abstract class NativePluginManager {
list(): NativePlugin[] { list(): NativePlugin[] {
@@ -40,19 +41,20 @@ export namespace server {
get(name: string): NativePlugin { get(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
load(name: string): boolean { enable(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
unload(name: string): boolean { disable(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
reload(name: string): boolean { reload(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
delete(name: string): boolean { delete(name: string): boolean {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
} }
/** /**
* MiaoScript Server * MiaoScript Server
*/ */
@@ -98,6 +100,7 @@ export namespace server {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
} }
@injectable() @injectable()
export class ServerChecker { export class ServerChecker {
@Autowired(ServerType) @Autowired(ServerType)
@@ -116,6 +119,22 @@ export namespace server {
} }
} }
} }
@injectable()
export class NativePluginChecker {
@Autowired(NativePluginManager)
private nativePluginManager: NativePluginManager
check(plugins: string[]) {
// Not set plugins -> allow
if (!plugins || !plugins.length) return true
for (const plugin of plugins) {
if (!this.nativePluginManager.has(plugin)) { return false }
}
return true
}
}
@injectable() @injectable()
export abstract class ReflectServer extends server.Server { export abstract class ReflectServer extends server.Server {
@Autowired(ContainerInstance) @Autowired(ContainerInstance)
@@ -172,13 +191,23 @@ export namespace server {
} }
protected reflectRootLogger(consoleServer: any) { protected reflectRootLogger(consoleServer: any) {
try { try {
this.rootLogger = reflect.on(consoleServer).get('LOGGER').get().parent this.rootLogger = reflect.on(consoleServer).get('LOGGER').get()
} catch (error: any) { } catch (error: any) {
if (global.debug) { if (global.debug) {
console.ex(error) console.ex(error)
} }
try { try {
this.rootLogger = reflect.on(consoleServer).get(0).get().parent this.rootLogger = reflect.on(consoleServer).get(0).get()
} catch (error: any) {
if (global.debug) {
console.ex(error)
}
}
}
if (this.rootLogger.class.name.indexOf('slf4j') !== -1) {
try {
let LogManager = Java.type('org.apache.logging.log4j.LogManager')
this.rootLogger = LogManager.getLogger('ROOT')
} catch (error: any) { } catch (error: any) {
if (global.debug) { if (global.debug) {
console.ex(error) console.ex(error)
@@ -189,11 +218,11 @@ export namespace server {
console.error('Error Logger Class: ' + this.rootLogger.class.name) console.error('Error Logger Class: ' + this.rootLogger.class.name)
this.rootLogger = undefined this.rootLogger = undefined
} }
if (!this.rootLogger) { console.error("Can't found rootLogger!") }
// get root logger // get root logger
for (let index = 0; index < 5 && this.rootLogger.parent; index++) { for (let index = 0; index < 5 && this.rootLogger.parent; index++) {
this.rootLogger = this.rootLogger.parent this.rootLogger = this.rootLogger.parent
} }
if (!this.rootLogger) { console.error("Can't found rootLogger!") }
this.container.bind(constants.ServiceIdentifier.RootLogger).toConstantValue(this.rootLogger) this.container.bind(constants.ServiceIdentifier.RootLogger).toConstantValue(this.rootLogger)
} }
} }

View File

@@ -1,6 +1,8 @@
import { plugin } from './index' import { EventEmitter } from 'events'
import { injectable } from '@ccms/container' import { injectable } from '@ccms/container'
import { plugin } from './index'
const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger")
export namespace task { export namespace task {
@@ -79,7 +81,7 @@ export namespace task {
/** /**
* 任务抽象 * 任务抽象
*/ */
export abstract class Task implements Cancelable { export abstract class Task extends EventEmitter implements Cancelable {
protected func: Function protected func: Function
protected isAsync: boolean = false; protected isAsync: boolean = false;
protected laterTime: number = 0; protected laterTime: number = 0;
@@ -88,7 +90,10 @@ export namespace task {
protected taskId: number protected taskId: number
protected innerTask: any protected innerTask: any
private cancelled: boolean = false
constructor(owner: plugin.Plugin, func: Function, id: number) { constructor(owner: plugin.Plugin, func: Function, id: number) {
super()
this.owner = owner this.owner = owner
this.func = func this.func = func
this.taskId = id this.taskId = id
@@ -134,20 +139,35 @@ export namespace task {
*/ */
cancel(): boolean { cancel(): boolean {
let result = this.cancel0() let result = this.cancel0()
process.emit('task.finish', this) this.finish()
this.cancelled = true
return result return result
} }
protected run(...args: any[]): void { protected run(...args: any[]): void {
try { try {
this.emit('before', this)
if (this.cancelled) { return }
this.func(...args) this.func(...args)
!this.interval && process.emit('task.finish', this) this.emit('after', this)
} catch (ex: any) { } catch (error: any) {
console.console('§4插件执行任务时发生错误', ex) this.emit('error', error)
console.ex(ex) if (!error.processed) {
console.console('§4插件执行任务时发生错误', error)
console.ex(error)
this.cancel()
}
} finally {
this.emit('finally', this)
if (!this.interval && !this.cancelled) { this.finish() }
} }
} }
protected finish() {
process.emit('task.finish', this)
this.emit('finish', this)
}
/** /**
* 提交任务 * 提交任务
* @param args 任务参数 * @param args 任务参数

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/bukkit", "name": "@ccms/bukkit",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript bukkit package", "description": "MiaoScript bukkit package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,12 +21,12 @@
"devDependencies": { "devDependencies": {
"@javatypes/spigot-api": "^0.0.3", "@javatypes/spigot-api": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
} }
} }

View File

@@ -1,7 +1,7 @@
import { channel, plugin } from '@ccms/api' import { channel, plugin } from '@ccms/api'
import { inject, provideSingleton } from '@ccms/container' import { inject, provideSingleton } from '@ccms/container'
const Bukkit = org.bukkit.Bukkit const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
const PluginMessageListener = Java.type("org.bukkit.plugin.messaging.PluginMessageListener") const PluginMessageListener = Java.type("org.bukkit.plugin.messaging.PluginMessageListener")
const Messenger = Bukkit.getMessenger() const Messenger = Bukkit.getMessenger()

View File

@@ -1,9 +1,12 @@
import { chat } from '@ccms/api' import { chat } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import bukkitChat from './enhance/chat' import bukkitChat from './internal/chat'
@provideSingleton(chat.Chat) @provideSingleton(chat.Chat)
export class BukkitChat extends chat.Chat { export class BukkitChat extends chat.Chat {
get handle(): any {
return bukkitChat
}
sendJson(sender: any, json: string | object, type = 0) { sendJson(sender: any, json: string | object, type = 0) {
bukkitChat.send(sender, typeof json === "string" ? json : JSON.stringify(json), type) bukkitChat.send(sender, typeof json === "string" ? json : JSON.stringify(json), type)
} }

View File

@@ -1,35 +1,38 @@
import '@ccms/nashorn' import '@ccms/nashorn'
import { command, plugin } from '@ccms/api' import { command, plugin } from '@ccms/api'
import * as reflect from '@ccms/common/dist/reflect'
import { provideSingleton, postConstruct, inject } from '@ccms/container' import { provideSingleton, postConstruct, inject } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect'
let Bukkit = org.bukkit.Bukkit const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
let TabCompleter = Java.type('org.bukkit.command.TabCompleter') const TabCompleter = Java.type('org.bukkit.command.TabCompleter')
let PluginCommand = Java.type('org.bukkit.command.PluginCommand') const PluginCommand = Java.type('org.bukkit.command.PluginCommand')
let CommandExecutor = Java.type('org.bukkit.command.CommandExecutor') const CommandExecutor = Java.type('org.bukkit.command.CommandExecutor')
@provideSingleton(command.Command) @provideSingleton(command.Command)
export class BukkitCommand extends command.Command { export class BukkitCommand extends command.Command {
@inject(plugin.PluginInstance) @inject(plugin.PluginInstance)
private pluginInstance: any private pluginInstance: any
private commandMap: any private commandMap: any
private knownCommands: any
@postConstruct() @postConstruct()
init() { init() {
this.commandMap = reflect.on(Bukkit.getPluginManager()).get('commandMap').get() this.commandMap = reflect.on(Bukkit.getPluginManager()).get('commandMap').get()
this.knownCommands = reflect.on(this.commandMap).get('knownCommands').get()
} }
create(plugin: any, command: string) { create(plugin: any, command: string) {
var cmd = this.commandMap.getCommand(command) var cmd = this.commandMap.getCommand(command)
if (cmd && cmd instanceof PluginCommand) { return cmd }; if (cmd instanceof PluginCommand) { return cmd };
cmd = reflect.on(PluginCommand).create(command, this.pluginInstance).get() cmd = reflect.on(PluginCommand).create(command, this.pluginInstance).get()
this.commandMap.register(plugin.description.name, cmd) this.commandMap.register(plugin.description.name, cmd)
return cmd return cmd
} }
remove(plugin: any, command: string) { remove(plugin: any, command: string) {
var cmd = this.commandMap.getCommand(command) var cmd = this.commandMap.getCommand(command)
if (cmd && cmd instanceof PluginCommand) { if (cmd instanceof PluginCommand) {
cmd.unregister(this.commandMap) cmd.unregister(this.commandMap)
this.knownCommands.remove(command)
} }
} }
tabComplete(sender: any, input: string, index?: number): string[] { tabComplete(sender: any, input: string, index?: number): string[] {

View File

@@ -1,20 +1,20 @@
import { MiaoScriptConsole } from '@ccms/api' import { MiaoScriptConsole } from '@ccms/api'
let Bukkit = org.bukkit.Bukkit; const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
export class BukkitConsole extends MiaoScriptConsole { export class BukkitConsole extends MiaoScriptConsole {
sender(sender, ...args) { sender(sender, ...args) {
if (!(sender instanceof Java.type('org.bukkit.command.CommandSender'))) { if (!(sender instanceof Java.type('org.bukkit.command.CommandSender'))) {
this.error(`First parameter ${sender} not instanceof org.bukkit.command.CommandSender can't send message!`) this.error(`First parameter ${sender} not instanceof org.bukkit.command.CommandSender can't send message!`)
return; return
} }
if (Object.prototype.toString.call(args[0]) === "[object Array]") { if (Object.prototype.toString.call(args[0]) === "[object Array]") {
args[0].forEach(line => sender.sendMessage(this.prefix + line)) args[0].forEach(line => sender.sendMessage(this.prefix + line))
} else { } else {
sender.sendMessage(this.prefix + args.join(' ')); sender.sendMessage(this.prefix + args.join(' '))
} }
} }
console(...args: string[]): void { console(...args: string[]): void {
this.sender(Bukkit.getConsoleSender(), args.join(' ')); this.sender(Bukkit.getConsoleSender(), args.join(' '))
} }
} }

View File

@@ -1,13 +1,14 @@
import { event, plugin } from '@ccms/api' import { event, plugin } from '@ccms/api'
import { inject, provideSingleton } from '@ccms/container'; import { inject, provideSingleton } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect' import * as reflect from '@ccms/common/dist/reflect'
const Bukkit = Java.type("org.bukkit.Bukkit"); const URL = Java.type('java.net.URL')
const Event = Java.type("org.bukkit.event.Event"); const Bukkit = Java.type("org.bukkit.Bukkit")
const Modifier = Java.type("java.lang.reflect.Modifier"); const Event = Java.type("org.bukkit.event.Event")
const Listener = Java.type("org.bukkit.event.Listener"); const Modifier = Java.type("java.lang.reflect.Modifier")
const EventPriority = Java.type("org.bukkit.event.EventPriority"); const Listener = Java.type("org.bukkit.event.Listener")
const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor"); const EventPriority = Java.type("org.bukkit.event.EventPriority")
const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor")
@provideSingleton(event.Event) @provideSingleton(event.Event)
export class BukkitEvent extends event.Event { export class BukkitEvent extends event.Event {
@@ -15,11 +16,19 @@ export class BukkitEvent extends event.Event {
private pluginInstance: any private pluginInstance: any
constructor() { constructor() {
super('org/bukkit/event'); super('org/bukkit/event')
} }
getJarFile(resource: string) { getJarFile(resource: string) {
return super.getJarFile('org/bukkit/Bukkit.class', Bukkit.class.classLoader) try {
return super.getJarFile('org/bukkit/Bukkit.class', Bukkit.class.classLoader)
} catch (error) {
// 兼容 LoliServer 的 Bukkit 包无法获取的问题
let ModList = Java.type('net.minecraftforge.fml.ModList').get()
let forgeFile = ModList.getModFileById("forge").getFile().getFilePath()
let jarPath = `jar:file:${forgeFile}!/org/bukkit/Bukkit.class`
return new URL(jarPath).openConnection().jarFile
}
} }
isValidEvent(clazz: any): boolean { isValidEvent(clazz: any): boolean {
// 继承于 org.bukkit.event.Event // 继承于 org.bukkit.event.Event
@@ -27,10 +36,10 @@ export class BukkitEvent extends event.Event {
// 访问符为Public // 访问符为Public
Modifier.isPublic(clazz.getModifiers()) && Modifier.isPublic(clazz.getModifiers()) &&
// 不是抽象类 // 不是抽象类
!Modifier.isAbstract(clazz.getModifiers()); !Modifier.isAbstract(clazz.getModifiers())
} }
register(eventCls: any, exec: Function, priority: event.EventPriority, ignoreCancel: boolean) { register(eventCls: any, exec: Function, priority: event.EventPriority, ignoreCancel: boolean) {
let listener = new Listener({}); let listener = new Listener({})
Bukkit.pluginManager.registerEvent( Bukkit.pluginManager.registerEvent(
eventCls, eventCls,
listener, listener,
@@ -39,10 +48,10 @@ export class BukkitEvent extends event.Event {
execute: exec execute: exec
}), }),
this.pluginInstance, this.pluginInstance,
ignoreCancel); ignoreCancel)
return listener; return listener
} }
unregister(event: any, listener: any): void { unregister(event: any, listener: any): void {
reflect.on(event).call('getHandlerList').get().unregister(listener); reflect.on(event).call('getHandlerList').get().unregister(listener)
} }
} }

View File

@@ -4,6 +4,7 @@ import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'
import { BukkitConsole } from './console' import { BukkitConsole } from './console'
import './item'
import './chat' import './chat'
import './task' import './task'
import './event' import './event'

View File

@@ -6,12 +6,14 @@ let bukkitChatInvoke: BukkitChatInvoke
abstract class BukkitChatInvoke { abstract class BukkitChatInvoke {
private downgrade: boolean = false private downgrade: boolean = false
protected RemapUtils: any protected RemapUtils: any
protected ComponentSerializer: any
protected ChatSerializer: any protected ChatSerializer: any
protected nmsChatSerializerMethodName: string protected nmsChatSerializerMethodName: string
protected PacketPlayOutChat: any protected PacketPlayOutChat: any
protected chatMessageTypes: any protected chatMessageTypes: any
protected playerConnectionFieldName: string protected playerConnectionFieldName: string
protected playerFieldName: string
protected sendPacketMethodName: string protected sendPacketMethodName: string
constructor(private nmsVersion) { constructor(private nmsVersion) {
@@ -20,30 +22,31 @@ abstract class BukkitChatInvoke {
init() { init() {
try { try {
try { try {
this.ComponentSerializer = Java.type('net.md_5.bungee.chat.ComponentSerializer')
this.RemapUtils = Java.type('catserver.server.remapper.RemapUtils') this.RemapUtils = Java.type('catserver.server.remapper.RemapUtils')
} catch (ex: any) { } catch (ex: any) {
} }
let nmsChatSerializerClass = this.getNmsChatSerializerClass() let nmsChatSerializerClass = this.getNmsChatSerializerClass()
let nmsChatSerializerMethod = this.remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String')) this.nmsChatSerializerMethodName = this.getNmsChatSerializerMethodName(nmsChatSerializerClass)
this.nmsChatSerializerMethodName = nmsChatSerializerMethod.getName()
this.ChatSerializer = Java.type(nmsChatSerializerClass.getName()) this.ChatSerializer = Java.type(nmsChatSerializerClass.getName())
let packetTypeClass = this.getPacketPlayOutChatClass() let packetTypeClass = this.getPacketPlayOutChatClass()
this.PacketPlayOutChat = Java.type(packetTypeClass.getName()) this.PacketPlayOutChat = Java.type(packetTypeClass.getName())
let packetTypeConstructor: { parameterTypes: any[] }
let constructors = packetTypeClass.constructors let constructors = packetTypeClass.constructors
Java.from(constructors).forEach(function (c) { for (const constructor of Java.from(constructors)) {
if (c.parameterTypes.length === 2 || c.parameterTypes.length === 3) { let parameterTypes = constructor.parameterTypes
packetTypeConstructor = c if (parameterTypes.length === 2 || parameterTypes.length === 3) {
let nmsChatMessageTypeClass = parameterTypes[1]
if (nmsChatMessageTypeClass.isEnum()) {
this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
break
} else if (nmsChatMessageTypeClass.getName() == 'int') {
break
}
} }
})
let parameterTypes = packetTypeConstructor.parameterTypes
let nmsChatMessageTypeClass = parameterTypes[1]
if (nmsChatMessageTypeClass.isEnum()) {
this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
} }
let playerConnectionField = this.getPlayerConnectionField() let playerConnectionField = this.getPlayerConnectionField()
this.playerConnectionFieldName = playerConnectionField.getName() this.playerConnectionFieldName = playerConnectionField.getName()
this.sendPacketMethodName = this.remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', this.getPacketClass()).getName() this.sendPacketMethodName = this.getSendPacketMethodName(playerConnectionField.getType())
} catch (ex: any) { } catch (ex: any) {
org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`) org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`)
this.downgrade = true this.downgrade = true
@@ -51,10 +54,12 @@ abstract class BukkitChatInvoke {
} }
abstract getNmsChatSerializerClass() abstract getNmsChatSerializerClass()
abstract getNmsChatSerializerMethodName(nmsChatSerializerClass: any)
abstract getPacketPlayOutChatClass() abstract getPacketPlayOutChatClass()
abstract getPacketPlayOutChat(sender: any, json: any, type: number) abstract getPacketPlayOutChat(sender: any, json: any, type: number)
abstract getPlayerConnectionField() abstract getPlayerConnectionField()
abstract getPacketClass() abstract getPacketClass()
abstract getSendPacketMethodName(playerConnectionClass: any)
nmsCls(name: string) { nmsCls(name: string) {
return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.')) return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.'))
@@ -84,9 +89,9 @@ abstract class BukkitChatInvoke {
} }
} }
json(sender: { name: string }, json: string) { json(sender: any, json: string) {
if (this.downgrade) { if (this.downgrade) {
return '/tellraw ' + sender.name + ' ' + json return sender.spigot().sendMessage(this.ComponentSerializer.parse(json))
} else { } else {
this.send(sender, json, 0) this.send(sender, json, 0)
return false return false
@@ -101,6 +106,13 @@ abstract class BukkitChatInvoke {
} }
class BukkitChatInvokeBase extends BukkitChatInvoke { class BukkitChatInvokeBase extends BukkitChatInvoke {
getSendPacketMethodName(playerConnectionClass: any) {
return this.remapMethod(playerConnectionClass, 'sendPacket', 'func_179290_a', this.getPacketClass()).getName()
}
getNmsChatSerializerMethodName(nmsChatSerializerClass: any) {
let nmsChatSerializerMethod = this.remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String'))
return nmsChatSerializerMethod.getName()
}
getPacketPlayOutChat(sender: any, json: any, type: number) { getPacketPlayOutChat(sender: any, json: any, type: number) {
return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type) return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type)
} }
@@ -149,17 +161,35 @@ class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 {
return base.getClass('net.minecraft.network.protocol.Packet') return base.getClass('net.minecraft.network.protocol.Packet')
} }
} }
class BukkitChatInvoke_1_18_2 extends BukkitChatInvoke_1_17_1 {
getSendPacketMethodName(playerConnectionClass: any) {
return playerConnectionClass.getMethod('a', this.getPacketClass()).getName()
}
}
class BukkitChatInvoke_1_19 extends BukkitChatInvoke_1_18_2 {
getPacketPlayOutChatClass() {
return base.getClass('net.minecraft.network.protocol.game.ClientboundSystemChatPacket')
}
getPacketPlayOutChat(sender: any, json: any, type: number) {
return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type)
}
}
try { try {
//@ts-ignore let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
let nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3] // @ts-ignore
let nmsVersion = Bukkit.getServer().class.name.split('.')[3]
let nmsSubVersion = nmsVersion.split("_")[1] let nmsSubVersion = nmsVersion.split("_")[1]
if (nmsSubVersion >= 8) { if (nmsSubVersion >= 19) {
bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion) bukkitChatInvoke = new BukkitChatInvoke_1_19(nmsVersion)
} else if (nmsSubVersion >= 16) { } else if (nmsSubVersion >= 18) {
bukkitChatInvoke = new BukkitChatInvoke_1_16_5(nmsVersion) bukkitChatInvoke = new BukkitChatInvoke_1_18_2(nmsVersion)
} else if (nmsSubVersion >= 17) { } else if (nmsSubVersion >= 17) {
bukkitChatInvoke = new BukkitChatInvoke_1_17_1(nmsVersion) bukkitChatInvoke = new BukkitChatInvoke_1_17_1(nmsVersion)
} else if (nmsSubVersion >= 16) {
bukkitChatInvoke = new BukkitChatInvoke_1_16_5(nmsVersion)
} else if (nmsSubVersion >= 8) {
bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion)
} else { } else {
bukkitChatInvoke = new BukkitChatInvoke_1_7_10(nmsVersion) bukkitChatInvoke = new BukkitChatInvoke_1_7_10(nmsVersion)
} }
@@ -168,6 +198,7 @@ try {
} }
let chat = { let chat = {
invoke: bukkitChatInvoke,
json: bukkitChatInvoke.json.bind(bukkitChatInvoke), json: bukkitChatInvoke.json.bind(bukkitChatInvoke),
send: bukkitChatInvoke.send.bind(bukkitChatInvoke) send: bukkitChatInvoke.send.bind(bukkitChatInvoke)
} }

File diff suppressed because it is too large Load Diff

128
packages/bukkit/src/item.ts Normal file
View File

@@ -0,0 +1,128 @@
import { item } from "@ccms/api"
import { provideSingleton } from "@ccms/container"
import { itemIds } from './internal/item'
const Material: typeof org.bukkit.Material = Java.type('org.bukkit.Material')
const ItemStack: typeof org.bukkit.inventory.ItemStack = Java.type('org.bukkit.inventory.ItemStack')
@provideSingleton(item.Item)
export class BukkitItem extends item.Item {
private CraftItemStack: any
private NBTTagCompound: any
private nmsSaveNBTMethodName: any
private MojangsonParser: any
private nmsItemStack: any
private mpParseMethodName: any
private nmsVersion: any
constructor() {
super()
this.reflect()
}
builder(): item.ItemBuilder {
return new BukkitItemBuilder()
}
toJson(item: any): string {
let nbt = new this.NBTTagCompound()
return this.CraftItemStack.asNMSCopy(item)[this.nmsSaveNBTMethodName](nbt).toString()
}
fromJson(json: string) {
return this.CraftItemStack.asBukkitCopy(new this.nmsItemStack(this.MojangsonParser[this.mpParseMethodName](json)))
}
private obcCls(name: string) {
return base.getClass(['org.bukkit.craftbukkit', this.nmsVersion, name].join('.'))
}
private nmsCls(name: string) {
return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.'))
}
private reflect() {
try {
let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
// @ts-ignore
this.nmsVersion = Bukkit.getServer().class.name.split('.')[3]
let CraftItemStackClass = this.obcCls('inventory.CraftItemStack')
this.CraftItemStack = Java.type(CraftItemStackClass.getName())
// @ts-ignore
let asNMSCopyMethod = CraftItemStackClass.getMethod('asNMSCopy', ItemStack.class)
let nmsItemStackClass = asNMSCopyMethod.getReturnType()
this.nmsItemStack = Java.type(nmsItemStackClass.getName())
let nmsNBTTagCompoundClass = undefined
for (let method of Java.from(nmsItemStackClass.getMethods())) {
let rt = method.getReturnType()
if (method.getParameterTypes().length == 0 && "NBTTagCompound" == rt.getSimpleName()) {
nmsNBTTagCompoundClass = rt
}
}
this.NBTTagCompound = Java.type(nmsNBTTagCompoundClass.getName())
for (let method of Java.from(nmsItemStackClass.getMethods())) {
let params = method.getParameterTypes()
let rt = method.getReturnType()
if (params.length == 1 && "NBTTagCompound" == params[0].getSimpleName() && "NBTTagCompound" == rt.getSimpleName()) {
this.nmsSaveNBTMethodName = method.getName()
break
}
}
try {
this.MojangsonParser = this.nmsCls('MojangsonParser').static
} catch (error) {
this.MojangsonParser = Java.type('net.minecraft.nbt.MojangsonParser')
}
for (let method of Java.from(this.MojangsonParser.class.getMethods())) {
let params = method.getParameterTypes()
let rt = method.getReturnType()
if (params.length == 1 && "String" == params[0].getSimpleName() && "NBTTagCompound" == rt.getSimpleName()) {
this.mpParseMethodName = method.getName()
}
}
} catch (error: any) {
console.log('Bukkit 物品管理器初始化失败:', error)
if (global.debug) {
console.ex(error)
}
}
}
}
export class BukkitItemBuilder implements item.ItemBuilder {
private itemStack: org.bukkit.inventory.ItemStack
private itemMeta: org.bukkit.inventory.meta.ItemMeta
from(itemStack: any): item.ItemBuilder {
this.itemStack = itemStack
this.itemMeta = this.itemStack.getItemMeta()
return this
}
create(type: string | number): item.ItemBuilder {
let material = undefined
if (typeof type == 'number') {
type = itemIds[type]
}
material = Material[type] || Material[Material['LEGACY_PREFIX'] + type]
this.itemStack = new ItemStack(material)
this.itemMeta = this.itemStack.getItemMeta()
return this
}
name(name: string): item.ItemBuilder {
this.itemMeta.setDisplayName(name)
return this
}
lore(...lores: string[]): item.ItemBuilder {
this.itemMeta.setLore(lores)
return this
}
amount(amount: number): item.ItemBuilder {
this.itemStack.setAmount(amount)
return this
}
durability(durability: number): item.ItemBuilder {
this.itemStack.setDurability(durability)
return this
}
clone() {
return this.build().clone()
}
build() {
this.itemStack.setItemMeta(this.itemMeta)
return this.itemStack
}
}

View File

@@ -1,22 +1,13 @@
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import { particle, plugin } from '@ccms/api' import { particle } from '@ccms/api'
@provideSingleton(particle.ParticleManager) @provideSingleton(particle.ParticleManager)
export class BukkitParticleManager extends particle.ParticleManager { export class BukkitParticleManager extends particle.ParticleManager {
private globalSpawner = new BukkitParticleSpawner()
constructor() {
super()
particle.ParticleManager.globalSpawner = this.globalSpawner
}
protected getGlobalSpawner() {
return this.globalSpawner
}
} }
@provideSingleton(particle.ParticleSpawner)
export class BukkitParticleSpawner extends particle.ParticleSpawner { export class BukkitParticleSpawner extends particle.ParticleSpawner {
spawnParticle(location: any, particle: any, count: number = 1) { spawn(location: org.bukkit.Location, particle: particle.Particle) {
location.getWorld().spawnParticle(particle, location, count)
}
spawn(location: any, particle: particle.Particle) {
location.getWorld().spawnParticle( location.getWorld().spawnParticle(
particle.getParticle(), particle.getParticle(),
location, location,
@@ -28,4 +19,15 @@ export class BukkitParticleSpawner extends particle.ParticleSpawner {
particle.getData() particle.getData()
) )
} }
spawnToPlayer(player: org.bukkit.entity.Player, location: org.bukkit.Location, particle: particle.Particle) {
player.spawnParticle(
particle.getParticle(),
location,
particle.getCount(),
particle.getOffsetX(),
particle.getOffsetY(),
particle.getOffsetZ(),
particle.getExtra(),
particle.getData())
}
} }

View File

@@ -19,6 +19,22 @@ export class BukkitNativePluginManager extends server.NativePluginManager {
get(name: string): server.NativePlugin { get(name: string): server.NativePlugin {
return this.convert(this.bukkitPluginManager.getPlugin(name)) return this.convert(this.bukkitPluginManager.getPlugin(name))
} }
enable(name: string): server.NativePlugin {
let origin = this.bukkitPluginManager.getPlugin(name)
if (!origin) { throw new Error(`Native Plugin ${name} not found.`) }
if (!origin.isEnabled()) {
this.bukkitPluginManager.enablePlugin(origin)
}
return this.convert(origin)
}
disable(name: string): server.NativePlugin {
let origin = this.bukkitPluginManager.getPlugin(name)
if (!origin) { throw new Error(`Native Plugin ${name} not found.`) }
if (origin.isEnabled()) {
this.bukkitPluginManager.disablePlugin(origin)
}
return this.convert(origin)
}
private convert(plugin: org.bukkit.plugin.Plugin): server.NativePlugin { private convert(plugin: org.bukkit.plugin.Plugin): server.NativePlugin {
if (!plugin) return plugin as any if (!plugin) return plugin as any

View File

@@ -1,10 +1,9 @@
import { server, constants } from '@ccms/api' import { server } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect' import * as reflect from '@ccms/common/dist/reflect'
import chat from './enhance/chat'
let Bukkit: typeof org.bukkit.Bukkit = org.bukkit.Bukkit const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
@provideSingleton(server.Server) @provideSingleton(server.Server)
export class BukkitServer extends server.ReflectServer { export class BukkitServer extends server.ReflectServer {

View File

@@ -1,5 +1,5 @@
import { task, plugin } from '@ccms/api' import { task, plugin } from '@ccms/api'
import { inject, provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
const Bukkit = Java.type('org.bukkit.Bukkit') const Bukkit = Java.type('org.bukkit.Bukkit')
const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable') const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable')

View File

@@ -2,6 +2,7 @@
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": "src", "baseUrl": "src",
"outDir": "dist" "outDir": "dist",
"resolveJsonModule": true
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/bungee", "name": "@ccms/bungee",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript bungee package", "description": "MiaoScript bungee package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,12 +21,12 @@
"devDependencies": { "devDependencies": {
"@javatypes/bungee-api": "^0.0.3", "@javatypes/bungee-api": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
} }
} }

View File

@@ -1 +0,0 @@
src/emp.ts

View File

@@ -1,37 +0,0 @@
{
"private": true,
"name": "@ccms/client",
"version": "0.18.0",
"description": "MiaoScript client package",
"keywords": [
"miaoscript",
"minecraft",
"bukkit",
"sponge"
],
"author": "MiaoWoo <admin@yumc.pw>",
"homepage": "https://github.com/circlecloud/ms.git",
"license": "ISC",
"main": "dist/index.js",
"scripts": {
"dev": "ts-node-dev --respawn --debounce=1500 src/index.ts",
"clean": "rimraf dist",
"watch": "tsc --watch",
"build": "yarn clean && tsc",
"start": "node dist/index.js",
"debug": "DEBUG=minecraft-protocol node dist/index.js",
"emp": "node dist/emp.js",
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"axios": "^0.25.0",
"minecraft-protocol": "^1.30.0",
"minecraft-protocol-forge": "^1.0.0",
"proxy-agent": "^5.0.0"
},
"devDependencies": {
"@types/node": "^17.0.17",
"rimraf": "^3.0.2",
"typescript": "^4.5.5"
}
}

View File

@@ -1,101 +0,0 @@
class MessagePart {
text: string
color: string
clickEvent: MessagePartEvent
hoverEvent: MessagePartEvent
translate: string
with: MessagePart[]
extra: MessagePart[]
}
class MessagePartEvent {
action: string
value: string
}
var colorMap = []
colorMap['0'] = '38;5;0'
colorMap['1'] = '38;5;4'
colorMap['2'] = '38;5;2'
colorMap['3'] = '38;5;6'
colorMap['4'] = '38;5;1'
colorMap['5'] = '38;5;5'
colorMap['6'] = '38;5;3'
colorMap['7'] = '38;5;7'
colorMap['8'] = '38;5;8'
colorMap['9'] = '38;5;12'
colorMap['a'] = '38;5;10'
colorMap['b'] = '38;5;14'
colorMap['c'] = '38;5;9'
colorMap['d'] = '38;5;13'
colorMap['e'] = '38;5;11'
colorMap['f'] = '38;5;15'
colorMap['r'] = '0'
colorMap['l'] = '1'
colorMap['n'] = '4'
var regexMap = []
for (const c in colorMap) {
regexMap[colorMap[c]] = new RegExp(`§${c}`, "g")
}
function mcColor2ANSI(str) {
for (const regex in regexMap) {
str = str.replace(regexMap[regex], `\u001b[${regex}m`)
}
return str;
}
let jsonColorMap = {
"black": '0',
"dark_blue": '1',
"dark_green": '2',
"dark_aqua": '3',
"dark_red": '4',
"dark_purple": '5',
"gold": '6',
"gray": '7',
"dark_gray": '8',
"blue": '9',
"green": 'a',
"aqua": 'b',
"red": 'c',
"light_purple": 'd',
"yellow": 'e',
"white": 'f',
"obfuscated": 'k',
"bold": 'l',
"strikethrough": 'm',
"underline": 'n',
"italic": 'o',
"reset": 'r',
};
function json2text(json: MessagePart): string {
let temp = "";
if (json.color) {
temp += `§${jsonColorMap[json.color]}`
}
temp += json.text || json.translate || ''
if (json.extra) {
json.extra.forEach((ext) => {
temp += json2text(ext)
})
}
return temp += '§r'
}
function $(input: any) {
if (typeof input === "string") {
input = JSON.parse(input)
}
input = json2text(input) + '§r'
if (input.startsWith('§卐')) {
input = input.substring(2)
}
return mcColor2ANSI(input)
}
export {
json2text,
mcColor2ANSI,
$
}

View File

@@ -1,33 +0,0 @@
import { $ } from './color'
export function attachEvents(client) {
client.on('chat', (packet) => {
// Listen for chat messages and echo them back.
var jsonMsg = JSON.parse(packet.message)
console.log($(jsonMsg))
})
client.on('state', (newState, oldState) => {
console.log('Client Change State', oldState, 'to', newState)
let targetServer = process.argv[6]
if (newState == "play" && targetServer) {
setTimeout(() => {
client.write('chat', {
message: '/server ' + targetServer
})
}, 3000)
}
})
client.on('update_health', (packet) => {
if (packet.health <= 0) {
console.log("Player Dead Auto Respawn...")
client.write('client_command', { payload: 0 })
} else if (packet.health > 0) {
}
})
client.on('kick_disconnect', (packet) => {
console.log($(packet.reason))
})
client.on('disconnect', (packet) => {
console.log($(packet.reason))
})
}

View File

@@ -1,14 +0,0 @@
export function attachForge(client) {
client.on('custom_payload', function(packet) {
if (packet.channel === 'FML|HS') {
client.write('custom_payload', {
channel: 'FML|HS',
data: Buffer.of(0x01, 0x02)
});
client.write('custom_payload', {
channel: 'FML|HS',
data: Buffer.of(0x02, 0x00)
});
}
});
}

View File

@@ -1,124 +0,0 @@
import { createInterface } from 'readline'
import { Client, createClient } from 'minecraft-protocol'
import { attachForge } from './forge'
import { attachEvents } from './event'
let readUserInfo = process.argv[2] || 'Mr_jtb'
let realUserInfo = readUserInfo.split(":")
let username = realUserInfo[0]
let password = realUserInfo[1] || ''
let version = process.argv[3] || '1.12.2'
let readAddress = process.argv[4] || '192.168.2.25:25565'
let realAddress = readAddress.split(":")
let address = realAddress[0]
let port = parseInt(realAddress[1] || "25565")
let client = commandLineCreateClient()
function commandLineCreateClient() {
return createConnection(address, port, username, password)
}
function createConnection(host: string, port: number, username: string, password: string) {
let clientOptions: any = {
version,
host,
port,
username,
password,
// clientToken: 'd02c7f39-2376-45da-a5a5-50e24fa8b185',
//@ts-ignore
// authServer: 'https://skin.yumc.pw/api/yggdrasil/authserver',
// sessionServer: 'https://skin.yumc.pw/api/yggdrasil/sessionserver'
}
if (clientOptions.password) {
clientOptions.clientToken = 'd02c7f39-2376-45da-a5a5-50e24fa8b185'
clientOptions.authServer = 'https://skin.yumc.pw/api/yggdrasil/authserver'
clientOptions.sessionServer = 'https://skin.yumc.pw/api/yggdrasil/sessionserver'
}
let client = createClient(clientOptions)
attachCommon(client)
attachForge(client)
attachEvents(client)
return client
}
function attachCommon(client: Client) {
client.on('login', () => {
// client.registerChannel('updater', ['string', []])
// client.registerChannel('updater-enabled', ['string', []])
// client.registerChannel('dragoncore', ['string', []])
// client.registerChannel('dragoncore:main', ['string', []])
client.on('REGISTER', (array) => {
for (const channel of array) {
client.on('channel', console.log)
}
})
// client.on('dragoncore:main', (data) => {
// console.log(data)
// })
})
client.on('custom_payload', (data) => {
console.log('custom_payload' + JSON.stringify(data))
})
client.on('error', (error) => {
console.log("Client Error", error)
})
client.on('end', (resone) => {
console.log("Client End Resone:", resone)
if (`${resone}` != "SocketClosed") {
setTimeout(() => {
client = commandLineCreateClient()
}, 500)
} else {
process.exit(0)
}
})
}
const rl = createInterface({
input: process.stdin,
output: process.stdout,
completer: (line, func) => {
let args = line.split(' ')
let comp = args[args.length - 1]
client.once('tab_complete', (msg) => {
let mcts = msg.matches.filter(s => s)
func(null, [mcts, comp])
})
client.write('tab_complete', {
text: line
})
},
terminal: true,
prompt: ''
})
rl.on('line', function (line) {
switch (line) {
case "":
break
case "eval":
break
case "write":
break
case "/respawn":
client.write('client_command', { payload: 0 })
break
case "//reco":
client.end("")
client = commandLineCreateClient()
break
case "//quit":
console.info('Disconnected')
client.end("")
break
case "//end":
console.info('Forcibly ended client')
process.exit(0)
default:
client.write('chat', { message: line })
}
rl.prompt()
})

View File

@@ -1,5 +0,0 @@
while :; do
yarn emp
echo 进程退出 休眠120秒!
sleep 120
done

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/common", "name": "@ccms/common",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript api package", "description": "MiaoScript common package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -19,11 +19,11 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"@javatypes/jdk": "^0.0.3", "@javatypes/jdk": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"gitHead": "562e2d00175c9d3a99c8b672aa07e6d92706a027" "gitHead": "562e2d00175c9d3a99c8b672aa07e6d92706a027"
} }

View File

@@ -19,12 +19,20 @@ interface RequestConfig {
method?: Method method?: Method
headers?: { [key: string]: string } headers?: { [key: string]: string }
params?: { [key: string]: string } params?: { [key: string]: string }
data?: any data?: any,
connectTimeout?: number,
readTimeout?: number,
} }
function request(config: RequestConfig) { function request(config: RequestConfig) {
// @ts-ignore XMLHttpRequest class only exist nashorn polyfill // @ts-ignore XMLHttpRequest class only exist nashorn polyfill
let xhr = new XMLHttpRequest() let xhr = new XMLHttpRequest()
if (config.connectTimeout) {
xhr.connectTimeout = config.connectTimeout
}
if (config.readTimeout) {
xhr.readTimeout = config.readTimeout
}
xhr.open(config.method, config.url, false) xhr.open(config.method, config.url, false)
for (const header in config.headers) { for (const header in config.headers) {
xhr.setRequestHeader(header, config.headers[header]) xhr.setRequestHeader(header, config.headers[header])

View File

@@ -54,6 +54,7 @@ class Reflect {
} }
} }
if (!field) throw new Error(`can't reflect field ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${this.class.getName()}!`) if (!field) throw new Error(`can't reflect field ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${this.class.getName()}!`)
fieldCache.set(key, field)
return accessible(field) return accessible(field)
} }
@@ -129,12 +130,14 @@ function declaredField(clazz: java.lang.Class<any>, name: string | java.lang.Str
let field = null let field = null
// noinspection JSUnresolvedVariable // noinspection JSUnresolvedVariable
while (target !== JavaObject.class) { while (target !== JavaObject.class) {
console.debug(`reflect field ${name} from ${target.getName()}`)
try { try {
field = target.getDeclaredField(name) field = target.getDeclaredField(name)
if (field !== null) { break } if (field !== null) { break }
} catch (error: any) { } catch (error: any) {
if (target === undefined) { break } if (target === undefined) { break }
target = target.getSuperclass() target = target.getSuperclass()
console.debug(`switch to super class: ${target.getName()}`)
} }
} }
if (field === null) { if (field === null) {
@@ -146,21 +149,32 @@ function declaredField(clazz: java.lang.Class<any>, name: string | java.lang.Str
function declaredMethod(clazz: java.lang.Class<any>, nameOrIndex: string | number, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method { function declaredMethod(clazz: java.lang.Class<any>, nameOrIndex: string | number, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method {
let key = clazz.getName() + '.' + nameOrIndex + ':' + (clazzs || []).map(c => c.getName()).join(':') let key = clazz.getName() + '.' + nameOrIndex + ':' + (clazzs || []).map(c => c.getName()).join(':')
if (methodCache.has(key)) { return methodCache.get(key) } if (methodCache.has(key)) { return methodCache.get(key) }
let target = clazz
if (typeof nameOrIndex === "number") { if (typeof nameOrIndex === "number") {
methodCache.set(key, declaredMethods(clazz)[nameOrIndex]) methodCache.set(key, declaredMethods(clazz)[nameOrIndex])
} else { } else {
try { while (target !== JavaObject.class && !methodCache.has(key)) {
methodCache.set(key, clazz.getMethod(nameOrIndex, clazzs as any))
} catch (ex: any) {
try { try {
methodCache.set(key, clazz.getDeclaredMethod(nameOrIndex, clazzs as any)) console.debug(`reflect method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${target.getName()}`)
} catch (ex: any) { try {
for (const m of Java.from(declaredMethods(clazz))) { methodCache.set(key, target.getMethod(nameOrIndex, clazzs as any))
if (m.getName() == nameOrIndex) { } catch (ex: any) {
methodCache.set(key, m) try {
break methodCache.set(key, target.getDeclaredMethod(nameOrIndex, clazzs as any))
} catch (ex: any) {
for (const m of Java.from(declaredMethods(target))) {
if (m.getName() == nameOrIndex) {
methodCache.set(key, m)
break
}
}
throw new Error(`method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} not found.`)
} }
} }
} catch (error) {
if (target === undefined) { break }
target = target.getSuperclass()
console.debug(`switch to super class: ${target.getName()}`)
} }
} }
} }
@@ -173,7 +187,7 @@ function declaredMethods(clazz: java.lang.Class<any>) {
} }
function mapToObject(javaObj) { function mapToObject(javaObj) {
if (!Java.isJavaObject(javaObj)) { throw new TypeError(`参数 ${javaObj} 不是一个Java对象!`) } if (!Java.isJavaObject(javaObj)) { throw new TypeError(`argument ${javaObj} is not a java object.`) }
let target = Proxy.newProxy(javaObj, { let target = Proxy.newProxy(javaObj, {
apply: (target, name, args) => { return args ? javaObj[name](args) : javaObj[name]() } apply: (target, name, args) => { return args ? javaObj[name](args) : javaObj[name]() }
}) })

View File

@@ -17,14 +17,14 @@ class ChatMessagePart {
} }
convert() { convert() {
return this.internal; return this.internal
} }
} }
class Tellraw { class Tellraw {
static duplicateChar = '§卐' static duplicateChar = '§卐'
static create() { static create() {
return new Tellraw().then(Tellraw.duplicateChar); return new Tellraw().then(Tellraw.duplicateChar)
} }
private cache: string = ''; private cache: string = '';
@@ -32,77 +32,81 @@ class Tellraw {
then(part: ChatMessagePart | string) { then(part: ChatMessagePart | string) {
if (typeof part === "string") { if (typeof part === "string") {
var newPart = new ChatMessagePart(); var newPart = new ChatMessagePart()
newPart.text = part newPart.text = part
this.then(newPart); this.then(newPart)
return this; return this
} }
var last = this.latest(); var last = this.latest()
if (!last.text) { if (!last.text) {
last.text = part.text; last.text = part.text
} else { } else {
this.parts.push(part); this.parts.push(part)
} }
this.cache = null; this.cache = null
} }
text(text: string) { text(text: string) {
this.latest().text = text; this.latest().text = text
return this; return this
} }
tip(text: string) { tip(texts: string) {
this.latest().hover("show_text", text); return this.hover(texts)
return this;
} }
item(text: string) { hover(texts: string) {
this.latest().hover("show_item", text); this.latest().hover("show_text", texts)
return this; return this
}
item(item: string) {
this.latest().hover("show_item", item)
return this
} }
command(command: string) { command(command: string) {
this.latest().click("run_command", command); this.latest().click("run_command", command)
return this; return this
} }
suggest(url: string) { suggest(url: string) {
this.latest().click("suggest_command", url); this.latest().click("suggest_command", url)
return this; return this
} }
file(path: string) { file(path: string) {
this.latest().click("open_file", path); this.latest().click("open_file", path)
return this; return this
} }
link(url: string) { link(url: string) {
this.latest().click("open_url", url); this.latest().click("open_url", url)
return this; return this
} }
latest() { latest() {
return this.parts[this.parts.length - 1]; return this.parts[this.parts.length - 1]
} }
json() { json() {
if (!this.cache) { if (!this.cache) {
var temp = []; var temp = []
this.parts.forEach(t => { this.parts.forEach(t => {
temp.push(t.convert()); temp.push(t.convert())
}); })
this.cache = JSON.stringify(temp); this.cache = JSON.stringify(temp)
console.trace(this.cache); console.trace(this.cache)
} }
return this.cache; return this.cache
} }
string() { string() {
var temp = ''; var temp = ''
this.parts.forEach(t => { this.parts.forEach(t => {
temp += t.text temp += t.text
}); })
return temp; return temp
} }
} }

View File

@@ -0,0 +1,35 @@
export type Version = [string, string, string]
export class VersionUtils {
static isEqual(version: string, targetVersion: string): boolean {
return version == targetVersion
}
static isGreaterOrEqual(version: string, targetVersion: string): boolean {
const v1 = parseVersion(version)
const v2 = parseVersion(targetVersion)
return (
v1[0] > v2[0] ||
(v1[0] === v2[0] && v1[1] > v2[1]) ||
(v1[0] === v2[0] && v1[1] === v2[1] && v1[2] >= v2[2])
)
}
static isGreater(version: string, targetVersion: string): boolean {
const v1 = parseVersion(version)
const v2 = parseVersion(targetVersion)
return (
v1[0] > v2[0] ||
(v1[0] === v2[0] && v1[1] > v2[1]) ||
(v1[0] === v2[0] && v1[1] === v2[1] && v1[2] > v2[2])
)
}
}
function parseVersion(version: string = ""): Version {
const v: Version = ['0', '0', '0']
version.split(".").forEach((value, i) => (v[i] = value))
return v
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/compile", "name": "@ccms/compile",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript compile package", "description": "MiaoScript compile package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -20,7 +20,7 @@
}, },
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/container", "name": "@ccms/container",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript container package", "description": "MiaoScript container package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,10 +19,10 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"inversify": "^6.0.1", "inversify": "^6.0.1",

View File

@@ -1,38 +1,9 @@
import { interfaces, Container } from "inversify" import { interfaces, Container } from "inversify"
import { _proxyGetter } from "./utils"
let _container: Container let _container: Container
const ContainerInstance = Symbol.for("@ccms/ioc:Container") const ContainerInstance = Symbol.for("@ccms/ioc:Container")
const INJECTION = Symbol.for("INJECTION")
function _proxyGetter(
proto: any,
key: string,
resolve: () => any,
doCache: boolean
) {
function getter(this: object) {
if (doCache && !Reflect.hasMetadata(INJECTION, this, key)) {
Reflect.defineMetadata(INJECTION, resolve(), this, key)
}
if (Reflect.hasMetadata(INJECTION, this, key)) {
return Reflect.getMetadata(INJECTION, this, key)
} else {
return resolve()
}
}
function setter(this: object, newVal: any) {
Reflect.defineMetadata(INJECTION, newVal, this, key)
}
Object.defineProperty(proto, key, {
configurable: true,
enumerable: true,
get: getter,
set: setter
})
}
function initContainer(container: Container) { function initContainer(container: Container) {
Reflect.defineMetadata(ContainerInstance, container, Reflect) Reflect.defineMetadata(ContainerInstance, container, Reflect)

View File

@@ -5,6 +5,7 @@ import { initContainer, getContainer } from './decorators'
import { interfaces, Container, inject, named } from 'inversify' import { interfaces, Container, inject, named } from 'inversify'
import { fluentProvide } from 'inversify-binding-decorators' import { fluentProvide } from 'inversify-binding-decorators'
import { ioc } from "./constants" import { ioc } from "./constants"
import { _proxyGetter } from "./utils"
/** /**
* 注册一个命名对象 * 注册一个命名对象
@@ -49,9 +50,11 @@ export const JavaClass = (className: string) => {
*/ */
export const JSClass = (className: string) => { export const JSClass = (className: string) => {
return function (target: object, propertyKey: string, index?: number) { return function (target: object, propertyKey: string, index?: number) {
try { target[propertyKey] = Java.type(className); return } catch (error: any) { } _proxyGetter(target, propertyKey, () => {
try { target[propertyKey] = base.getClass(className).static; return } catch (error: any) { } try { return Java.type(className) } catch (error: any) { }
console.warn('JSClass', className, 'Inject target', target.constructor.name, 'propertyKey', propertyKey, 'failed!') try { return base.getClass(className).static } catch (error: any) { }
console.warn('JSClass', className, 'Inject target', target.constructor.name, 'propertyKey', propertyKey, 'failed!')
}, true)
} }
} }
@@ -88,6 +91,59 @@ export const Resource = (resourceName?: string | any) => {
} }
} }
const DocumentBuilderFactory = Java.type('javax.xml.parsers.DocumentBuilderFactory')
export const MavenDepend = (groupId: string, artifactId: string, version: string, recursion = false) => {
return function (target: any) {
loadMavenDepend(groupId, artifactId, version, recursion)
}
}
const loadedMavenDepend = new Set<string>()
export function loadMavenDepend(groupId: string, artifactId: string, version: string, recursion = false) {
const key = `${groupId}:${artifactId}:${version}`
try {
if (loadedMavenDepend.has(key)) { return }
console.info('loading maven dependency', key)
let [pom, _] = base.loadMavenDepend(groupId, artifactId, version)
loadedMavenDepend.add(key)
if (recursion) {
let doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(pom)
let dependencies = doc.getElementsByTagName("dependency")
let size = dependencies.length
if (!size) { return }
console.debug(key, 'found', size, 'dependencies loading...')
for (let i = 0; i < size; i++) {
const dependency = dependencies.item(i)
const gav = dependency.getChildNodes()
const length = gav.length
const dependencyVersion = { groupId: '', artifactId: '', version: '' }
for (let j = 0; j < length; j++) {
const prop = gav.item(j)
switch (prop.getNodeName()) {
case "groupId":
dependencyVersion.groupId = prop.getTextContent()
break
case "artifactId":
dependencyVersion.artifactId = prop.getTextContent()
break
case "version":
dependencyVersion.version = prop.getTextContent()
break
}
}
loadMavenDepend(dependencyVersion.groupId, dependencyVersion.artifactId, dependencyVersion.version, recursion)
}
}
} catch (error: any) {
console.warn('load maven dependency', key, 'failed. Error:', error)
if (global.debug) {
console.ex(error)
}
}
}
export const reduceMetadata = (ctx: interfaces.Context): any => { export const reduceMetadata = (ctx: interfaces.Context): any => {
return ctx.currentRequest.target.metadata.reduce((result, entry, index) => { return ctx.currentRequest.target.metadata.reduce((result, entry, index) => {
result[entry.key] = entry.value result[entry.key] = entry.value
@@ -98,8 +154,10 @@ export const reduceMetadata = (ctx: interfaces.Context): any => {
function initAutowired(container: Container) { function initAutowired(container: Container) {
container.bind(ioc.Autowired).toDynamicValue((ctx) => { container.bind(ioc.Autowired).toDynamicValue((ctx) => {
var metadata: any = reduceMetadata(ctx) var metadata: any = reduceMetadata(ctx)
let key = Object.toString.call(metadata.named) let key = Object.prototype.toString.call(metadata.named)
if (key === "[object Function]" || key === "[object Symbol]") { return container.get(metadata.named) } if (key === "[object Function]" || key === "[object Symbol]") { return container.get(metadata.named) }
console.warn('container Autowired', metadata.named, 'failed. Error: illegal serviceIdentifier type', key)
console.debug(metadata.named, 'metadata', JSON.stringify(metadata))
return undefined return undefined
}) })
} }

View File

@@ -0,0 +1,30 @@
const INJECTION = Symbol.for("INJECTION")
export function _proxyGetter(
proto: any,
key: string,
resolve: () => any,
doCache: boolean
) {
function getter(this: object) {
if (doCache && !Reflect.hasMetadata(INJECTION, this, key)) {
Reflect.defineMetadata(INJECTION, resolve(), this, key)
}
if (Reflect.hasMetadata(INJECTION, this, key)) {
return Reflect.getMetadata(INJECTION, this, key)
} else {
return resolve()
}
}
function setter(this: object, newVal: any) {
Reflect.defineMetadata(INJECTION, newVal, this, key)
}
Object.defineProperty(proto, key, {
configurable: true,
enumerable: true,
get: getter,
set: setter
})
}

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/core", "name": "@ccms/core",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript api package", "description": "MiaoScript core package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -20,12 +20,12 @@
}, },
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

1
packages/core/src/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
script

View File

@@ -1,9 +1,15 @@
let containerStartTime = Date.now() let containerStartTime = Date.now()
console.i18n("ms.core.ioc.initialize", { scope: global.scope }) console.i18n("ms.core.ioc.initialize", { scope: global.scope })
import { plugin, server, task, constants } from '@ccms/api' import { plugin, server, task, constants, console as jsconsole } from '@ccms/api'
import { DefaultContainer as container, provideSingleton, ContainerInstance, buildProviderModule, Autowired } from '@ccms/container' import { DefaultContainer as container, provideSingleton, ContainerInstance, buildProviderModule, Autowired } from '@ccms/container'
console.i18n("ms.core.ioc.completed", { scope: global.scope, time: (Date.now() - containerStartTime) / 1000 }) console.i18n("ms.core.ioc.completed", { scope: global.scope, time: (Date.now() - containerStartTime) / 1000 })
import * as yaml from 'js-yaml'
import http from '@ccms/common/dist/http' import http from '@ccms/common/dist/http'
import * as fs from '@ccms/common/dist/fs'
import { VersionUtils } from '@ccms/common/dist/version'
const UUID = Java.type('java.util.UUID')
const MiaoScriptAPI = Java.type('pw.yumc.MiaoScript.api.MiaoScriptAPI')
@provideSingleton(MiaoScriptCore) @provideSingleton(MiaoScriptCore)
class MiaoScriptCore { class MiaoScriptCore {
@@ -17,17 +23,31 @@ class MiaoScriptCore {
private pluginManager: plugin.PluginManager private pluginManager: plugin.PluginManager
enable() { enable() {
process.emit('core.before.enable')
this.loadServerConsole() this.loadServerConsole()
try {
MiaoScriptAPI.setPluginManager(this.pluginManager)
} catch (error) {
}
this.loadPlugins() this.loadPlugins()
process.emit('core.after.enable')
console.i18n("ms.core.engine.completed", {
loader: base.version,
version: 'v' + global.ScriptEngineVersion,
time: (Date.now() - global.ScriptEngineStartTime) / 1000
})
return () => this.disable() return () => this.disable()
} }
loadServerConsole() { loadServerConsole() {
process.emit('core.before.load.console')
//@ts-ignore //@ts-ignore
global.setGlobal('console', new this.Console(), { writable: false, configurable: false }) global.setGlobal('console', new this.Console(), { writable: false, configurable: false })
process.emit('core.after.load.console')
} }
loadPlugins() { loadPlugins() {
process.emit('core.before.load.plugins')
let loadPluginStartTime = new Date().getTime() let loadPluginStartTime = new Date().getTime()
console.i18n("ms.core.plugin.initialize") console.i18n("ms.core.plugin.initialize")
this.pluginManager.scan(this.pluginFolder) this.pluginManager.scan(this.pluginFolder)
@@ -35,15 +55,25 @@ class MiaoScriptCore {
this.pluginManager.load(this.pluginManager.getPlugins()) this.pluginManager.load(this.pluginManager.getPlugins())
this.pluginManager.enable(this.pluginManager.getPlugins()) this.pluginManager.enable(this.pluginManager.getPlugins())
console.i18n("ms.core.plugin.completed", { time: (new Date().getTime() - loadPluginStartTime) / 1000 }) console.i18n("ms.core.plugin.completed", { time: (new Date().getTime() - loadPluginStartTime) / 1000 })
process.emit('core.after.load.plugins')
} }
disable() { disable() {
process.emit('core.before.disable')
let disableStartTime = Date.now() let disableStartTime = Date.now()
console.i18n("ms.core.engine.disable") console.i18n("ms.core.engine.disable")
this.pluginManager.disable(this.pluginManager.getPlugins()) this.pluginManager.disable(this.pluginManager.getPlugins())
this.taskManager.disable() this.taskManager.disable()
process.emit('core.after.disable')
loadCoreScript('exit')
process.emit('core.before.exit')
process.exit(0) process.exit(0)
console.i18n("ms.core.engine.disable.finish", { version: 'v' + global.ScriptEngineVersion, time: (new Date().getTime() - disableStartTime) / 1000 }) console.i18n("ms.core.engine.disable.finish", {
loader: base.version,
version: 'v' + global.ScriptEngineVersion,
time: (new Date().getTime() - disableStartTime) / 1000
})
process.emit('core.after.exit')
} }
} }
@@ -76,31 +106,76 @@ function detectServer(): constants.ServerType {
throw Error('Unknow Server Type...') throw Error('Unknow Server Type...')
} }
function initialize() { function loadCoreScript(name) {
global.ScriptEngineVersion = require('../package.json').version
try { engineLoad({ script: http.get("http://ms.yumc.pw/api/plugin/download/name/initialize"), name: 'core/initialize.js' }) } catch (error: any) { console.debug(error) }
try { try {
let corePackageStartTime = new Date().getTime() let scriptname = name + (global.debug ? '-debug' : '')
container.bind(ContainerInstance).toConstantValue(container) engineLoad({
container.bind(plugin.PluginInstance).toConstantValue(base.getInstance()) script: http.get(`https://mscript.yumc.pw/api/plugin/download/name/${scriptname}`),
container.bind(plugin.PluginFolder).toConstantValue('plugins') name: `core/${scriptname}.js`
let type = detectServer() })
console.i18n("ms.core.initialize.detect", { scope: global.scope, type })
container.bind(server.ServerType).toConstantValue(type)
container.bind(server.ServerChecker).toSelf().inSingletonScope()
container.bind(server.NativePluginManager).toSelf().inSingletonScope()
console.i18n("ms.core.package.initialize", { scope: global.scope, type })
require(`${global.scope}/${type}`).default(container)
require(`${global.scope}/plugin`)
container.load(buildProviderModule())
console.i18n("ms.core.package.completed", { scope: global.scope, type, time: (Date.now() - corePackageStartTime) / 1000 })
let disable = container.get<MiaoScriptCore>(MiaoScriptCore).enable()
console.i18n("ms.core.engine.completed", { version: 'v' + global.ScriptEngineVersion, time: (Date.now() - global.ScriptEngineStartTime) / 1000 })
return disable
} catch (error: any) { } catch (error: any) {
console.i18n("ms.core.initialize.error", { error }) if (global.debug) {
console.ex(error) console.debug(error)
return () => console.i18n('ms.core.engine.disable.abnormal') console.ex(error)
}
}
}
function loadMiaoScriptConfig() {
let configFile = fs.concat(root, 'config.yml')
if (!fs.exists(configFile)) {
global.ScriptEngineConfig = base.save(configFile, yaml.dump({
uuid: UUID.randomUUID().toString(),
slow_execute: 50
}))
} else {
global.ScriptEngineConfig = yaml.load(base.read(configFile))
}
global.ScriptSlowExecuteTime = global.ScriptEngineConfig.slow_execute || 50
}
function createCore() {
let corePackageStartTime = new Date().getTime()
container.bind(ContainerInstance).toConstantValue(container)
container.bind(plugin.PluginInstance).toConstantValue(base.getInstance())
container.bind(plugin.PluginFolder).toConstantValue('plugins')
let type = detectServer()
process.emit('core.before.initialize.detect')
console.i18n("ms.core.initialize.detect", { scope: global.scope, type })
container.bind(server.ServerType).toConstantValue(type)
container.bind(server.ServerChecker).toSelf().inSingletonScope()
container.bind(server.NativePluginManager).toSelf().inSingletonScope()
container.bind(server.NativePluginChecker).toSelf().inSingletonScope()
process.emit('core.after.initialize.detect')
process.emit('core.before.package.initialize')
console.i18n("ms.core.package.initialize", { scope: global.scope, type })
require(`${global.scope}/${type}`).default(container)
require(`${global.scope}/plugin`)
container.load(buildProviderModule())
console.i18n("ms.core.package.completed", { scope: global.scope, type, time: (Date.now() - corePackageStartTime) / 1000 })
process.emit('core.after.package.initialize')
return container.get<MiaoScriptCore>(MiaoScriptCore)
}
function initialize() {
process.emit('core.before.initialize')
loadMiaoScriptConfig()
global.ScriptEngineVersion = require('../package.json').version
global.setGlobal('loadCoreScript', loadCoreScript)
loadCoreScript('initialize')
try {
let core = createCore()
return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable()
} catch (error: any) {
let core = { enable: () => () => console.i18n('ms.core.engine.disable.abnormal') }
console.i18n("core.initialize.error", { error })
jsconsole.getStackTrace(error, false).forEach(line => console.log(line))
process.emit('core.initialize.error')
return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable()
} finally {
process.emit('core.after.initialize')
} }
} }

View File

@@ -1,38 +0,0 @@
function initialize() {
var mspmc = 'http://ms.yumc.pw/api/plugin/download/name/'
var http = require('@ccms/common/dist/http').default
var fs = require('@ccms/common/dist/fs')
var pluginFolder = fs.concat(root, 'plugins')
var updateFolder = fs.concat(pluginFolder, 'update')
var pluginFile = fs.concat(pluginFolder, 'MiaoScriptPackageManager.js')
if (!fs.exists(pluginFile)) {
fs.mkdirs(pluginFile)
base.save(fs.concat(updateFolder, 'MiaoScriptPackageManager.auto.install'), '.')
}
fs.list(updateFolder).forEach(function (path) {
var file = path.toFile()
if (file.exists()) {
var filename = file.getName()
if (filename.endsWith(".auto.install")) {
var pluginName = filename.replace('.auto.install', '')
var pluginFile = fs.concat(pluginFolder, pluginName + '.js')
if (!fs.exists(pluginFile)) {
var pluginTemp = pluginFile + '.tmp'
http.download(mspmc + pluginName, pluginTemp)
fs.move(pluginTemp, pluginFile, true)
}
base.delete(file)
}
}
})
var core = http.get('https://registry.npmmirror.com/@ccms/core')
if (base.VERSION && global.ScriptEngineVersion != core['dist-tags']['latest']) {
var Paths = Java.type('java.nio.file.Paths')
base.save(Paths.get(root, "upgrade"), core['dist-tags']['latest'])
console.info('@ccms/core found new version ' + core['dist-tags']['latest'] + ' will upgrade after reboot!')
}
console.debug('initialize finish!')
}
initialize()

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/database", "name": "@ccms/database",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript database package", "description": "MiaoScript database package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,11 +21,11 @@
"devDependencies": { "devDependencies": {
"@javatypes/spring-jdbc": "^0.0.3", "@javatypes/spring-jdbc": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
} }
} }

View File

@@ -1,88 +1,109 @@
import { Model } from './model' import { database } from '@ccms/api'
import { JSClass } from '@ccms/container'
const HikariDataSource = Java.type('com.zaxxer.hikari.HikariDataSource') const Thread = Java.type('java.lang.Thread')
const HikariConfig = Java.type('com.zaxxer.hikari.HikariConfig') const JavaString = Java.type('java.lang.String')
const JdbcTemplate = Java.type('org.springframework.jdbc.core.JdbcTemplate') const Properties = Java.type('java.util.Properties')
/**
* 数据库配置
*/
export interface DataBaseConfig {
/**
* 数据库连接串
*/
url: string | javax.sql.DataSource
/**
* 数据库驱动
*/
driverClassName?: string
/**
* 用户名
*/
username?: string
/**
* 密码
*/
password?: string
}
/** /**
* 数据库封装类 * 数据库封装类
*/ */
export class DataBase { export class DataBase extends database.DataBase {
private dataSource: javax.sql.DataSource private dataSource: javax.sql.DataSource
private jdbcTemplate: org.springframework.jdbc.core.JdbcTemplate private jdbcTemplate: org.springframework.jdbc.core.JdbcTemplate
constructor(dbConfig: DataBaseConfig) { @JSClass('com.zaxxer.hikari.HikariDataSource')
private HikariDataSource: any
@JSClass('com.zaxxer.hikari.HikariConfig')
private HikariConfig: any
@JSClass('org.springframework.jdbc.core.JdbcTemplate')
private JdbcTemplate: typeof org.springframework.jdbc.core.JdbcTemplate
constructor(dbConfig: database.DataBaseConfig) {
super()
if (!dbConfig.url) { throw new Error('DataBase url can\'t be null!') } if (!dbConfig.url) { throw new Error('DataBase url can\'t be null!') }
this.createDataSource(dbConfig) this.createDataSource(dbConfig)
this.initialize()
} }
private createDataSource(dbConfig: DataBaseConfig) { private createDataSource(dbConfig: database.DataBaseConfig) {
if (typeof dbConfig.url === "string") { if (typeof dbConfig.url === "string") {
if (!dbConfig.username || !dbConfig.password) { let originClassLoader = Thread.currentThread().getContextClassLoader()
throw new Error('DataBase username or password can\'t be null!') Thread.currentThread().setContextClassLoader(base.getInstance().class.classLoader)
} let config = new this.HikariConfig()
let config = new HikariConfig()
if (dbConfig.driverClassName) { if (dbConfig.driverClassName) {
config.setDriverClassName(dbConfig.driverClassName) config.setDriverClassName(dbConfig.driverClassName)
} else {
switch (dbConfig.type) {
case "h2":
config.setDriverClassName("org.h2.Driver")
break
case "sqlite":
config.setDriverClassName("org.sqlite.JDBC")
break
default:
}
}
if (dbConfig.username) {
config.setUsername(dbConfig.username)
}
if (dbConfig.password) {
config.setPassword(dbConfig.password)
} }
config.setUsername(dbConfig.username)
config.setPassword(dbConfig.password)
config.setJdbcUrl(dbConfig.url) config.setJdbcUrl(dbConfig.url)
this.dataSource = new HikariDataSource(config) if (dbConfig.properties) {
let properties = new Properties()
for (const key in dbConfig.properties) {
properties.setProperty(key, dbConfig.properties[key])
}
config.setDataSourceProperties(properties)
}
console.debug('createDataSource from config ' + JSON.stringify(dbConfig))
this.dataSource = new this.HikariDataSource(config)
Thread.currentThread().setContextClassLoader(originClassLoader)
} else { } else {
this.dataSource = dbConfig.url this.dataSource = dbConfig.url
} }
this.jdbcTemplate = new this.JdbcTemplate(this.dataSource)
} }
private initialize() { getDataSource() {
this.jdbcTemplate = new JdbcTemplate(this.dataSource) return this.dataSource
} }
/** /**
* 执行SQL查询 * 执行SQL查询
* @param sql SQL语句 * @param sql SQL语句
* @param args 参数
*/ */
query<T>(sql: string, ...args: any[]): Array<T> { query<T>(sql: string, ...args: any[]): Array<T> {
let startTime = Date.now() let startTime = Date.now()
let result = Java.from<any>(this.jdbcTemplate.queryForList(sql, args)) let result = Java.from<any>(this.jdbcTemplate.queryForList(sql, args))
console.debug(java.lang.String.format(`\n[DB] query \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args)) console.debug(JavaString.format(`\n[DB] query \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args))
return result return result
} }
/** /**
* 执行SQL更新 * 执行SQL更新
* @param sql SQL语句 * @param sql SQL语句
* @param args 参数
*/ */
update(sql: string, ...args: any[]): number { update(sql: string, ...args: any[]): number {
let startTime = Date.now() let startTime = Date.now()
let result = this.jdbcTemplate.update(sql, args) let result = this.jdbcTemplate.update(sql, args)
console.debug(java.lang.String.format(`\n[DB] update \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args)) console.debug(JavaString.format(`\n[DB] update \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args))
return result return result
} }
/**
* 执行SQL语句
* @param sql SQL语句
*/
execute(sql: string): void {
let startTime = Date.now()
this.jdbcTemplate.execute(sql)
console.debug(`\n[DB] execute \nSQL : ${sql} \nCOST : ${Date.now() - startTime}ms`)
}
close() { close() {
//@ts-ignore //@ts-ignore
this.dataSource.close() this.dataSource.close()

View File

@@ -1,7 +0,0 @@
import 'reflect-metadata'
export function id() {
return (target: Object, propertyKey: string | symbol) => void {
}
}

View File

@@ -2,5 +2,11 @@
/// <reference types="@javatypes/jdk" /> /// <reference types="@javatypes/jdk" />
/// <reference types="@javatypes/spring-jdbc" /> /// <reference types="@javatypes/spring-jdbc" />
import { loadMavenDepend } from '@ccms/container'
loadMavenDepend('com.h2database', 'h2', '2.1.212')
loadMavenDepend("com.zaxxer", "HikariCP", "4.0.3")
loadMavenDepend("org.springframework", "spring-jdbc", "5.3.19", true)
export * from './database' export * from './database'
export * from './manager' export * from './manager'

View File

@@ -1,27 +1,46 @@
import { plugin, database } from '@ccms/api' import { database } from '@ccms/api'
import { provideSingleton, inject, postConstruct } from '@ccms/container' import { JSClass, provideSingleton } from '@ccms/container'
import { DataBase, DataBaseConfig } from './database' import { DataBase } from './database'
@provideSingleton(database.DataBaseManager) @provideSingleton(database.DataBaseManager)
export class DataBaseManager { export class DataBaseManager extends database.DataBaseManager {
@inject(plugin.PluginInstance)
private instance: any
private beanFactory: any
private mainDatabase: DataBase private mainDatabase: DataBase
private databases: { [key: string]: DataBase } = {} private databases = new Map<string, DataBase>()
@postConstruct() @JSClass('org.h2.tools.Server')
initialize() { private Server: any
try {
this.beanFactory = this.instance.getAutowireCapableBeanFactory() private webManager: any
let mainDatasource = this.beanFactory.getBean(Packages.javax.sql.DataSource.class)
this.mainDatabase = new DataBase({ url: mainDatasource }) constructor() {
} catch (error: any) { super()
console.ex(error) process.on('exit', () => this.shutdown())
}
startWebManager(...args: string[]) {
this.webManager = this.Server.createWebServer(args)
this.webManager.start()
}
stopWebManager() {
if (this.webManager) {
this.webManager.stop()
this.webManager.shutdown()
} }
} }
shutdown() {
this.stopWebManager()
this.disable()
}
/**
* 设置主数据库
* @param mainDatabase 主数据库
*/
setMainDatabase(mainDatabase: DataBase) {
this.mainDatabase = mainDatabase
}
/** /**
* 获得主数据库 * 获得主数据库
* Get MainDatabase * Get MainDatabase
@@ -36,18 +55,28 @@ export class DataBaseManager {
* @param name 数据库名称 用于代码 database Name use at code * @param name 数据库名称 用于代码 database Name use at code
* @param config 数据库配置 * @param config 数据库配置
*/ */
createDatabase(name: string, config: DataBaseConfig) { createDatabase(name: string, config: database.DataBaseConfig) {
Java.synchronized(() => { return Java.synchronized(() => {
if (this.databases[name]) return this.databases[name] if (!this.databases.has(name)) {
return this.databases[name] = new DataBase(config) this.databases.set(name, new DataBase(config))
}
return this.databases.get(name)
}, this.databases)() }, this.databases)()
} }
removeDatabase(name: string) {
if (this.databases.has(name)) {
this.databases.get(name).close()
}
return this.databases.delete(name)
}
getDatabase(name: string) { getDatabase(name: string) {
return this.databases[name] return this.databases.get(name)
} }
disable() { disable() {
Object.values(this.databases).forEach((ds) => ds?.close()) this.databases.forEach((db) => db.close())
this.databases.clear()
} }
} }

View File

@@ -1,11 +0,0 @@
import { DataBase } from "./database"
export class Model<T> {
constructor(private database: DataBase) {
}
queryForList(): Array<T> {
return []
}
}

View File

@@ -12,6 +12,7 @@ ms.core.plugin.initialize: "Initialization MiaoScript Plugin System. Please wait
ms.core.plugin.completed: "MiaoScript Plugin System loading completed({time}s)!" ms.core.plugin.completed: "MiaoScript Plugin System loading completed({time}s)!"
ms.core.engine.completed: "MiaoScript ScriptEngine loading completed... Done({time}s)!" ms.core.engine.completed: "MiaoScript ScriptEngine loading completed... Done({time}s)!"
ms.core.engine.disable: "Disable MiaoScript Engine..." ms.core.engine.disable: "Disable MiaoScript Engine..."
ms.core.engine.disable.finish: "MiaoScript framework {loader} engine {version} 关闭完成... 耗时({time}s)!"
ms.core.engine.disable.abnormal: "abnormal Initialization MiaoScript Engine. Skip disable step..." ms.core.engine.disable.abnormal: "abnormal Initialization MiaoScript Engine. Skip disable step..."
ms.api.event.resource.not.found: "Can't Mapping Event Because not found Resources {resource}!" ms.api.event.resource.not.found: "Can't Mapping Event Because not found Resources {resource}!"
@@ -21,16 +22,20 @@ ms.api.event.not.found: "§6Plugin §b{name} §6register {event} error. event no
ms.api.event.execute.slow: "§cWARN! §6Plugin §b{name} §6execute §d{event} §6evnet §ccost §4{cost}ms !" ms.api.event.execute.slow: "§cWARN! §6Plugin §b{name} §6execute §d{event} §6evnet §ccost §4{cost}ms !"
ms.api.event.execute.error: "§6Plugin §b{name} §6execute §d{event} §6event error §4{ex}" ms.api.event.execute.error: "§6Plugin §b{name} §6execute §d{event} §6event error §4{ex}"
ms.api.event.listen.plugin.name.empty: "Plugin name can't be empty!" ms.api.event.listen.plugin.name.empty: "Plugin name can't be empty!"
ms.api.event.register: "[{name}] register event {event}" ms.api.event.register: "[{name}] register event {event} priority {priority} ignoreCancelled {ignore}"
ms.api.event.unregister: "[{name}] unregister event {event}" ms.api.event.unregister: "[{name}] unregister event {event}"
ms.api.command.register.input.error: "CommandExec Must be a function... Input: {exec}" ms.api.command.register.input.error: "CommandExec Must be a function... Input: {exec}"
ms.api.command.register: "[{plugin}] register command {name}({cmd})..." ms.api.command.register: "[{plugin}] register command {name}({cmd})..."
ms.api.command.unregister: "[{plugin}] unregister command {name}..." ms.api.command.unregister: "[{plugin}] unregister command {name}..."
ms.api.command.execute.slow: "§cWarn. §6Player §a{player} §6exec §b{plugin} §6Plugin §d{command} {args} §6Command §cCost §4{cost}ms !"
ms.api.command.execute.error: "§6Player {player} §6exec §b{plugin} §6Plugin Command §d{command} {args} §6error §4{ex}" ms.api.command.execute.error: "§6Player {player} §6exec §b{plugin} §6Plugin Command §d{command} {args} §6error §4{ex}"
ms.api.command.tab.completer.slow: "§cWarn. §6Player §a{player} §6exec §b{plugin} §6Plugin §d{command} {args} §6TabComplete §cCost §4{cost}ms !"
ms.api.command.tab.completer.error: "§6Player {player} §6exec §b{plugin} §6Plugin TabComplete §d{command} {args} §6error §4{ex}" ms.api.command.tab.completer.error: "§6Player {player} §6exec §b{plugin} §6Plugin TabComplete §d{command} {args} §6error §4{ex}"
ms.plugin.initialize: "Initialization MiaoScript Plugin System: Plugin: {plugin} Loader: {loader}..." ms.plugin.initialize: "Initialization MiaoScript Plugin System: Plugin: {plugin} Loader: {loader}..."
ms.plugin.event.map: "Total {count} {type} Event Mapping Complate..." ms.plugin.event.map: "Total {count} {type} Event Mapping Complate..."
ms.plugin.event.map.error: "Mapping {type} Event Failed. Error: {error}"
ms.plugin.manager.scan: "Scanner {scanner} Scanning Plugins in {folder} ..." ms.plugin.manager.scan: "Scanner {scanner} Scanning Plugins in {folder} ..."
ms.plugin.manager.scan.finish: "Scanner {scanner} Found {size} Plugins in {folder} Start Build..." ms.plugin.manager.scan.finish: "Scanner {scanner} Found {size} Plugins in {folder} Start Build..."
ms.plugin.manager.initialize.error: "§6Plugin §b{name} §6initialize error §4{ex}" ms.plugin.manager.initialize.error: "§6Plugin §b{name} §6initialize error §4{ex}"

View File

@@ -10,9 +10,9 @@ ms.core.package.initialize: "初始化 MiaoScript 扩展 {scope}/core {scope}/{t
ms.core.package.completed: "MiaoScript 扩展 {scope}/core {scope}/{type} {scope}/plugin 加载完成 耗时({time}s)" ms.core.package.completed: "MiaoScript 扩展 {scope}/core {scope}/{type} {scope}/plugin 加载完成 耗时({time}s)"
ms.core.plugin.initialize: "MiaoScript 开始引导插件系统. 请稍候..." ms.core.plugin.initialize: "MiaoScript 开始引导插件系统. 请稍候..."
ms.core.plugin.completed: "MiaoScript 插件加载完毕 耗时({time}s)!" ms.core.plugin.completed: "MiaoScript 插件加载完毕 耗时({time}s)!"
ms.core.engine.completed: "MiaoScript 脚本引擎 {version} 加载完毕... 耗时({time}s)!" ms.core.engine.completed: "MiaoScript 框架 {loader} 引擎 {version} 加载完毕... 耗时({time}s)!"
ms.core.engine.disable: "MiaoScript 关闭脚本引擎 请稍候..." ms.core.engine.disable: "MiaoScript 关闭脚本引擎 请稍候..."
ms.core.engine.disable.finish: "MiaoScript 脚本引擎 {version} 关闭完成... 耗时({time}s)!" ms.core.engine.disable.finish: "MiaoScript 框架 {loader} 引擎 {version} 关闭完成... 耗时({time}s)!"
ms.core.engine.disable.abnormal: "引擎异常启动或初始化未完成 跳过关闭流程..." ms.core.engine.disable.abnormal: "引擎异常启动或初始化未完成 跳过关闭流程..."
ms.api.event.resource.not.found: "无法映射事件 未找到资源文件 {resource}!" ms.api.event.resource.not.found: "无法映射事件 未找到资源文件 {resource}!"
@@ -22,18 +22,22 @@ ms.api.event.not.found: "§6插件 §b{name} §6注册事件 §c{event} §6失
ms.api.event.execute.slow: "§c注意! §6插件 §b{name} §6处理 §d{event} §6事件 §c耗时 §4{cost}ms !" ms.api.event.execute.slow: "§c注意! §6插件 §b{name} §6处理 §d{event} §6事件 §c耗时 §4{cost}ms !"
ms.api.event.execute.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}" ms.api.event.execute.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}"
ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!" ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!"
ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec}" ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec} 优先级 {priority} 忽略取消 {ignore}"
ms.api.event.unregister: "[{name}] 注销事件 {event} => 执行器 {exec}" ms.api.event.unregister: "[{name}] 注销事件 {event} => 执行器 {exec}"
ms.api.command.register.input.error: "CommandExec 必须为一个函数... 输入: {exec}" ms.api.command.register.input.error: "CommandExec 必须为一个函数... 输入: {exec}"
ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..." ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..."
ms.api.command.unregister: "[{plugin}] 注销命令 {name}..." ms.api.command.unregister: "[{plugin}] 注销命令 {name}..."
ms.api.command.execute.slow: "§c注意! §6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6命令 §c耗时 §4{cost}ms !"
ms.api.command.execute.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6命令时发生异常 §4{ex}" ms.api.command.execute.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6命令时发生异常 §4{ex}"
ms.api.command.tab.completer.slow: "§c注意! §6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全 §c耗时 §4{cost}ms !"
ms.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全时发生异常 §4{ex}" ms.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全时发生异常 §4{ex}"
ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..." ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..."
ms.plugin.event.map: "总计 {count} 个 {type} 事件 映射完成..." ms.plugin.event.map: "映射 {type} 事件 成功 总计 {count} 个 {type} 事件..."
ms.plugin.event.map.error: "映射 {type} 事件 异常 将无法使用事件简称. Error: {error}"
ms.plugin.manager.scan: "扫描器 {scanner} 扫描 {folder} 中的插件..." ms.plugin.manager.scan: "扫描器 {scanner} 扫描 {folder} 中的插件..."
ms.plugin.manager.scan.finish: "扫描器 {scanner} 在 {folder} 中 发现 {size} 个插件 开始构建..." ms.plugin.manager.scan.finish: "扫描器 {scanner} 在 {folder} 中 发现 {size} 个插件 开始编译..."
ms.plugin.manager.initialize.error: "§6插件 §b{name} §6初始化错误 §4{ex}" ms.plugin.manager.initialize.error: "§6插件 §b{name} §6初始化错误 §4{ex}"
ms.plugin.manager.stage: "{stage} {plugin} 版本 {version} 作者 {author}" ms.plugin.manager.stage: "{stage} {plugin} 版本 {version} 作者 {author}"
ms.plugin.manager.stage.exec: "[{plugin}] 执行 {stage} 阶段函数 {name} 匹配类型 {servers}..." ms.plugin.manager.stage.exec: "[{plugin}] 执行 {stage} 阶段函数 {name} 匹配类型 {servers}..."
@@ -41,7 +45,7 @@ ms.plugin.manager.stage.exec.error: "§6插件 §b{plugin} §6执行 §d{executo
ms.plugin.manager.stage.load: "加载" ms.plugin.manager.stage.load: "加载"
ms.plugin.manager.stage.enable: "启用" ms.plugin.manager.stage.enable: "启用"
ms.plugin.manager.stage.disable: "关闭" ms.plugin.manager.stage.disable: "关闭"
ms.plugin.manager.build: "插件 {name}({version}) 构建完成 来源: {file}({scanner}) 引导: {loader} 构建耗时: {cost}s." ms.plugin.manager.build: "插件 {name}({version}) 编译完成 来源: {file}({scanner}) 引导: {loader} 编译耗时: {cost}s."
ms.plugin.manager.build.error: "§6从文件 §b{file} §6加载插件失败 §4错误: §c{error}" ms.plugin.manager.build.error: "§6从文件 §b{file} §6加载插件失败 §4错误: §c{error}"
ms.plugin.manager.build.update: "自动更新插件 {name} ..." ms.plugin.manager.build.update: "自动更新插件 {name} ..."
ms.plugin.manager.build.not.extends: "§4发现错误的插件 §b{source} §4未继承接口 interfaces.Plugin, 将不会被载入到服务器!" ms.plugin.manager.build.not.extends: "§4发现错误的插件 §b{source} §4未继承接口 interfaces.Plugin, 将不会被载入到服务器!"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/i18n", "name": "@ccms/i18n",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript i18n package", "description": "MiaoScript i18n package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,11 +19,11 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/keyvalue", "name": "@ccms/keyvalue",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript keyvalue package", "description": "MiaoScript keyvalue package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,18 +19,18 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"@javatypes/amqp-client": "^0.0.3", "@javatypes/amqp-client": "^0.0.3",
"@javatypes/spring-amqp": "^0.0.3", "@javatypes/spring-amqp": "^0.0.3",
"@javatypes/spring-rabbit": "^0.0.3", "@javatypes/spring-rabbit": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"gitHead": "2589633069d24f646ac09261b1b2304c21d4ea75" "gitHead": "2589633069d24f646ac09261b1b2304c21d4ea75"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/molang", "name": "@ccms/molang",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "A fast parser for Minecraft's MoLang", "description": "A fast parser for Minecraft's MoLang",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -21,9 +21,9 @@
}, },
"homepage": "https://github.com/solvedDev/MoLang#readme", "homepage": "https://github.com/solvedDev/MoLang#readme",
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.17", "@types/node": "^18.13.0",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"tslib": "^2.3.1", "tslib": "^2.5.0",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/nashorn", "name": "@ccms/nashorn",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript api package", "description": "MiaoScript nashorn package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -21,7 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -48,9 +48,34 @@ declare global {
scope: string scope: string
logger: any logger: any
debug: boolean debug: boolean
level: string /**
* 引擎日志等级
*/
ScriptEngineLoggerLevel: string
/**
* 引擎配置
*/
ScriptEngineConfig: any
/**
* 引擎版本
*/
ScriptEngineVersion: string ScriptEngineVersion: string
/**
* 引擎渠道
*/
ScriptEngineChannel: string
/**
* 慢执行检测时间
*/
ScriptSlowExecuteTime: number
ScriptEngineStartTime: number ScriptEngineStartTime: number
/**
* 设置全局对象
* @param key 对象名称
* @param value 对象值
* @param config 对象属性
* @returns
*/
setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType<any>) => void setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType<any>) => void
noop: () => void noop: () => void
console: Console console: Console
@@ -65,10 +90,12 @@ declare global {
const ScriptEngineContextHolder: any const ScriptEngineContextHolder: any
function engineLoad(str: string | { script: string, name: string }): any function engineLoad(str: string | { script: string, name: string }): any
interface Core { interface Core {
version: string
getClass(name: String): any getClass(name: String): any
getProxyClass(): any getProxyClass(): any
getJavaScriptTaskClass(): any getJavaScriptTaskClass(): any
getInstance(): any getInstance(): any
loadMavenDepend(groupId: string, artifactId: string, version: string): [any, any]
read(path: string): string read(path: string): string
save(path: string, content: string): void save(path: string, content: string): void
delete(path: string): void delete(path: string): void

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nodejs", "name": "@ccms/nodejs",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript nodejs package", "description": "MiaoScript nodejs package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,11 +19,11 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"tslib": "^2.3.1", "tslib": "^2.5.0",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

View File

@@ -20,6 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict'; 'use strict';
var Throwable = Java.type('java.lang.Throwable')
var R = typeof Reflect === 'object' ? Reflect : null var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function' var ReflectApply = R && typeof R.apply === 'function'
? R.apply ? R.apply
@@ -136,13 +137,19 @@ EventEmitter.prototype.emit = function emit(type) {
var er; var er;
if (args.length > 0) if (args.length > 0)
er = args[0]; er = args[0];
if (er instanceof Error) { if (er instanceof Error || er instanceof Throwable) {
// Note: The comments on the `throw` lines are intentional, they show // Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception. // up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event throw er; // Unhandled 'error' event
} }
if (er.error instanceof Error || er.error instanceof Throwable) {
throw er.error; // Unhandled 'error' event
}
if (er.cause instanceof Error || er.error instanceof Throwable) {
throw er.error; // Unhandled 'error' event
}
// At least give some kind of context to the user // At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); var err = new Error('Unhandled error.' + (er ? ' (' + (er.message || er.error || er.cause || er) + ')' : ''));
// @ts-ignore // @ts-ignore
err.context = er; err.context = er;
throw err; // Unhandled 'error' event throw err; // Unhandled 'error' event

View File

@@ -5,6 +5,7 @@ const Path = Java.type("java.nio.file.Path");
const JavaString = Java.type("java.lang.String"); const JavaString = Java.type("java.lang.String");
const File = Java.type("java.io.File"); const File = Java.type("java.io.File");
const Files = Java.type("java.nio.file.Files"); const Files = Java.type("java.nio.file.Files");
const Paths = Java.type("java.nio.file.Paths");
const Collector = Java.type("java.util.stream.Collector") const Collector = Java.type("java.util.stream.Collector")
const separatorChar = File.separatorChar; const separatorChar = File.separatorChar;
const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption"); const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption");
@@ -37,7 +38,7 @@ function javaFile(...opts: any[]) {
} }
export function renameSync(oldPath: PathLike, newPath: PathLike): void { export function renameSync(oldPath: PathLike, newPath: PathLike): void {
Files.move(Paths.get(oldPath), Paths.get(oldPath), StandardCopyOption['ATOMIC_MOVE'])
} }
export function truncateSync() { export function truncateSync() {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nukkit", "name": "@ccms/nukkit",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript nukkit package", "description": "MiaoScript nukkit package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,12 +21,12 @@
"devDependencies": { "devDependencies": {
"@javatypes/nukkit-api": "^0.0.3", "@javatypes/nukkit-api": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0" "@ccms/container": "^0.28.0-beta.2"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/plugin", "name": "@ccms/plugin",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript api package", "description": "MiaoScript plugin package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -19,17 +19,19 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.1.1",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.18.0", "@ccms/api": "^0.28.0-beta.2",
"@ccms/common": "^0.18.0", "@ccms/common": "^0.28.0-beta.2",
"@ccms/container": "^0.18.0", "@ccms/container": "^0.28.0-beta.2",
"@ccms/i18n": "^0.18.0", "@ccms/i18n": "^0.28.0-beta.2",
"js-yaml": "^4.1.0", "@ccms/verify": "^0.25.1",
"yaml": "^1.10.2" "crypto-js": "^4.1.1",
"js-yaml": "^4.1.0"
} }
} }

View File

@@ -15,52 +15,90 @@ export class PluginCommandManager {
process.on('plugin.after.disable', this.unregistryCommand.bind(this)) process.on('plugin.after.disable', this.unregistryCommand.bind(this))
} }
private registryCommand(pluginInstance: plugin.Plugin) { public registryCommand(pluginInstance: plugin.Plugin, executor: any = pluginInstance) {
let cmds = getPluginCommandMetadata(pluginInstance) let cmds = getPluginCommandMetadata(executor)
let tabs = getPluginTabCompleterMetadata(pluginInstance) let tabs = getPluginTabCompleterMetadata(executor)
for (const cmd of cmds) { for (const cmd of cmds) {
if (!this.ServerChecker.check(cmd.servers)) { if (!this.ServerChecker.check(cmd.servers)) {
console.debug(`[${pluginInstance.description.name}] ${cmd.target.constructor.name} incompatible command ${cmd.name} server(${cmd.servers}) ignore.`) console.debug(`[${pluginInstance.description.name}] ${cmd.target.constructor.name} incompatible command ${cmd.name} server(${cmd.servers}) ignore.`)
continue continue
} }
for (let command of [cmd.name, ...cmd.alias]) { for (let command of [cmd.name, ...cmd.alias]) {
let [cmdExecutor, cmdCompleter] = this.generateAutoMainCommand(pluginInstance, cmd, tabs.get(command)) let [cmdExecutor, cmdCompleter] = this.generateAutoMainCommand(pluginInstance, executor, cmd, tabs.get(command))
this.CommandManager.on(pluginInstance, command, { this.CommandManager.on(pluginInstance, command, {
cmd: cmdExecutor.bind(pluginInstance), cmd: cmdExecutor.bind(executor),
tab: cmdCompleter?.bind(pluginInstance) tab: cmdCompleter?.bind(executor)
}) })
} }
} }
} }
private unregistryCommand(pluginInstance: plugin.Plugin) { public unregistryCommand(pluginInstance: plugin.Plugin, executor: any = pluginInstance) {
let cmds = getPluginCommandMetadata(pluginInstance) let cmds = getPluginCommandMetadata(executor)
cmds.forEach(cmd => this.CommandManager.off(pluginInstance, cmd.name)) for (const cmd of cmds) {
if (!this.ServerChecker.check(cmd.servers)) {
console.debug(`[${pluginInstance.description.name}] ${cmd.target.constructor.name} incompatible command ${cmd.name} server(${cmd.servers}) ignore.`)
continue
}
for (let command of [cmd.name, ...cmd.alias]) {
this.CommandManager.off(executor, command)
}
}
} }
private generateAutoMainCommand(pluginInstance: plugin.Plugin, cmd: interfaces.CommandMetadata, tab: interfaces.CommandMetadata) { private generateAutoMainCommand(pluginInstance: plugin.Plugin, executor: any, cmd: interfaces.CommandMetadata, tab: interfaces.CommandMetadata) {
let cmdExecutor = pluginInstance[cmd.executor] let cmdExecutor = executor[cmd.executor]
let cmdCompleter = tab ? pluginInstance[tab.executor] : undefined let cmdCompleter = tab ? executor[tab.executor] : undefined
let cmdSubCache = Object.keys(pluginInstance.constructor.prototype).filter(s => s.startsWith('cmd')).map(s => s.substring(3)) let cmdSubCache = Object.keys(executor.constructor.prototype).filter(s => s.startsWith('cmd')).map(s => s.substring(3))
if (cmd.autoMain) { if (cmd.autoMain) {
cmdExecutor = (sender: any, command: string, args: string[]) => { cmdExecutor = (sender: any, command: string, args: string[]) => {
let subcommand = args[0] let subcommand = args[0]
let cmdKey = 'cmd' + subcommand let cmdKey = 'cmd' + subcommand
if (pluginInstance[cmdKey]) { if (!cmdSubCache.includes(subcommand)) {
args.shift() if (!executor[cmd.executor].apply(executor, [sender, command, args])) {
return pluginInstance[cmdKey].apply(pluginInstance, [sender, ...args]) subcommand && pluginInstance.logger.sender(sender, `§4未知的命令: §b/${command} §c${subcommand}`)
} else if (pluginInstance['cmdmain']) { pluginInstance.logger.sender(
return pluginInstance['cmdmain'].apply(pluginInstance, [sender, ...args]) sender,
executor['cmdhelp'] ?
`§6请执行 §b/${command} §ahelp §6查看帮助!` :
[
`§6插件: §b${pluginInstance.description.name}`,
`§6版本: §a${pluginInstance.description.version}`
]
)
}
return
} }
pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) let subcommandexec = executor[cmdKey]
pluginInstance['cmdhelp'] && pluginInstance.logger.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`) let permission: string
if (cmd.permission && sender.hasPermission) {
if (typeof cmd.permission == "string") {
permission = cmd.permission as string
} else {
permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand || 'main'}`
}
if (!sender.hasPermission(permission)) {
return pluginInstance.logger.sender(sender, `§c你需要 §4${permission} §c权限 才可执行此命令.`)
}
}
args.shift()
return subcommandexec.apply(executor, [sender, ...args])
} }
let originCompleter = cmdCompleter let originCompleter = cmdCompleter
cmdCompleter = (sender: any, command: string, args: string[]) => { cmdCompleter = (sender: any, command: string, args: string[]) => {
return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(pluginInstance, [sender, command, args]) || []) let permission: string
if (cmd.permission && sender.hasPermission) {
if (typeof cmd.permission == "string") {
permission = cmd.permission as string
} else {
permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}`
}
if (!sender.hasPermission(permission)) { return [] }
}
return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(executor, [sender, command, args]) || [])
} }
} }
if (!cmdCompleter) { console.warn(`[${pluginInstance.description.name}] command ${cmd.name} is not registry tabCompleter`) } if (!cmdCompleter) { console.debug(`[${pluginInstance.description.name}] command ${cmd.name} is not registry tabCompleter`) }
return [cmdExecutor, cmdCompleter] return [cmdExecutor, cmdCompleter]
} }
} }

View File

@@ -1,59 +1,35 @@
import * as yaml from 'js-yaml'
import * as fs from '@ccms/common/dist/fs'
import { plugin } from '@ccms/api' import { plugin } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { Autowired, Container, ContainerInstance, postConstruct, provideSingleton } from '@ccms/container'
import * as fs from '@ccms/common/dist/fs'
import { interfaces } from './interfaces' import { interfaces } from './interfaces'
import { getPluginConfigMetadata } from './utils' import { getPluginConfigMetadata } from './utils'
export interface PluginConfigLoader { import { PluginConfigLoader } from './config/interfaces'
load(content: string): any import './config/loader'
dump(variable: any): string
}
export class YamlPluginConfig implements PluginConfigLoader {
load(content: string) {
return yaml.load(content)
}
dump(variable: any): string {
return yaml.dump(variable, { skipInvalid: true, lineWidth: 120 })
}
}
export class JsonPluginConfig implements PluginConfigLoader {
load(content: string) {
return JSON.parse(content)
}
dump(variable: any): string {
return JSON.stringify(variable, undefined, 4)
}
}
export interface PluginConfig {
/**
* Save Config to File
*/
readonly save?: () => void
/**
* Reload Config from File
*/
readonly reload?: () => void
[key: string]: any
}
@provideSingleton(PluginConfigManager) @provideSingleton(PluginConfigManager)
export class PluginConfigManager { export class PluginConfigManager {
@Autowired(ContainerInstance)
private container: Container
private configLoaderMap = new Map<string, PluginConfigLoader>() private configLoaderMap = new Map<string, PluginConfigLoader>()
constructor() { constructor() {
this.configLoaderMap.set("json", new JsonPluginConfig())
let yaml = new YamlPluginConfig()
this.configLoaderMap.set("yml", yaml)
this.configLoaderMap.set("yaml", yaml)
process.on('plugin.before.load', this.loadConfig.bind(this)) process.on('plugin.before.load', this.loadConfig.bind(this))
process.on('plugin.after.disable', this.saveConfig.bind(this)) process.on('plugin.after.disable', this.saveConfig.bind(this))
} }
@postConstruct()
initialize() {
let configLoader = this.container.getAll<PluginConfigLoader>(PluginConfigLoader)
configLoader.forEach((scanner) => {
console.debug(`loading config loader ${scanner.type}...`)
this.configLoaderMap.set(scanner.type, scanner)
})
}
getConfigLoader(format: string) { getConfigLoader(format: string) {
if (!this.configLoaderMap.has(format)) { throw new Error(`Unsupport config format ${format} !`) } if (!this.configLoaderMap.has(format)) { throw new Error(`Unsupport config format ${format} !`) }
return this.configLoaderMap.get(format) return this.configLoaderMap.get(format)
@@ -73,50 +49,65 @@ export class PluginConfigManager {
} }
} }
private defienConfigProp(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) { createConfig(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) {
Object.defineProperties(value, { Object.defineProperties(value, {
'save': { value: () => this.saveConfig0(plugin, metadata) }, 'save': { value: () => this.saveConfig0(plugin, metadata) },
'reload': { value: () => this.loadConfig0(plugin, metadata) } 'reload': { value: () => this.loadConfig0(plugin, metadata) }
}) })
Object.defineProperty(plugin, metadata.variable, { value }) return value
}
private defienConfigProp(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) {
Object.defineProperty(plugin, metadata.variable, {
value: this.createConfig(plugin, metadata, value),
configurable: true
})
} }
private loadConfig0(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata) { private loadConfig0(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata) {
try { try {
let defaultValue = metadata.default ?? plugin[metadata.variable] let defaultValue = metadata.default ?? plugin[metadata.variable]
let configValue = defaultValue || {} metadata.file = fs.concat(
if (defaultValue) { fs.file(plugin.description.loadMetadata.file).parent,
metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename) plugin.description.name,
let configLoader = this.getConfigLoader(metadata.format) metadata.filename
if (!fs.exists(metadata.file)) { )
base.save(metadata.file, configLoader.dump(defaultValue)) let configLoader = this.getConfigLoader(metadata.format)
console.i18n("ms.plugin.manager.config.save.default", { plugin: plugin.description.name, name: metadata.name, format: metadata.format }) if (!fs.exists(metadata.file) && defaultValue) {
} else { base.save(metadata.file, configLoader.dump(defaultValue))
configValue = configLoader.load(base.read(metadata.file)) || {} console.i18n("ms.plugin.manager.config.save.default", {
if (defaultValue && this.setDefaultValue(configValue, defaultValue)) { plugin: plugin.description.name,
base.save(metadata.file, configLoader.dump(configValue)) name: metadata.name,
} format: metadata.format
console.debug(`[${plugin.description.name}] Load Config ${metadata.variable} from file ${metadata.file} =>\n${JSON.stringify(configValue, undefined, 4).substr(0, 500)}`) })
}
} }
let configValue = configLoader.load(base.read(metadata.file)) || {}
if (metadata.migrate && defaultValue && this.setDefaultValue(configValue, defaultValue, !!metadata.default)) {
base.save(metadata.file, configLoader.dump(configValue))
}
console.debug(`[${plugin.description.name}] Load Config ${metadata.variable} from file ${metadata.file}`)
this.defienConfigProp(plugin, metadata, configValue) this.defienConfigProp(plugin, metadata, configValue)
} catch (error: any) { } catch (error: any) {
console.i18n("ms.plugin.manager.config.load.error", { plugin: plugin.description.name, name: metadata.name, format: metadata.format, error }) console.i18n("ms.plugin.manager.config.load.error", {
plugin: plugin.description.name,
name: metadata.name,
format: metadata.format,
error
})
console.ex(error) console.ex(error)
} }
} }
private setDefaultValue(configValue, defaultValue) { private setDefaultValue(configValue, defaultValue, deepCopy) {
let needSave = false let needSave = false
for (const key of Object.keys(defaultValue)) { for (const key of Object.keys(defaultValue)) {
// 当配置文件不存在当前属性时才进行赋值 // 当配置文件不存在当前属性时才进行赋值
if (!Object.prototype.hasOwnProperty.call(configValue, key) && key != '____deep_copy____') { if (!Object.prototype.hasOwnProperty.call(configValue, key)) {
configValue[key] = defaultValue[key] configValue[key] = defaultValue[key]
needSave = true needSave = true
} else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" && deepCopy) {
&& Object.prototype.hasOwnProperty.call(defaultValue[key], '____deep_copy____')) { // 对象需要递归检测
// 对象需要递归检测 如果对象内存在 ____deep_copy____ 那就忽略设置 needSave ||= this.setDefaultValue(configValue[key], defaultValue[key], deepCopy)
needSave ||= this.setDefaultValue(configValue[key], defaultValue[key])
} }
} }
return needSave return needSave
@@ -127,10 +118,16 @@ export class PluginConfigManager {
metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename) metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename)
let result = this.getConfigLoader(metadata.format).dump(plugin[metadata.variable]) let result = this.getConfigLoader(metadata.format).dump(plugin[metadata.variable])
base.save(metadata.file, result) base.save(metadata.file, result)
console.debug(`[${plugin.description.name}] Save Config ${metadata.variable} to file ${metadata.file} =>\n${result.substr(0, 500)}`) console.debug(`[${plugin.description.name}] Save Config ${metadata.variable} to file ${metadata.file} =>
${result.substring(0, 500)}`)
return true return true
} catch (error: any) { } catch (error: any) {
console.i18n("ms.plugin.manager.config.save.error", { plugin: plugin.description.name, name: metadata.name, format: metadata.format, error }) console.i18n("ms.plugin.manager.config.save.error", {
plugin: plugin.description.name,
name: metadata.name,
format: metadata.format,
error
})
console.ex(error) console.ex(error)
return false return false
} }

View File

@@ -0,0 +1,3 @@
export * from "./loader"
export * from "./interfaces"
export * from "./file-config"

View File

@@ -0,0 +1,65 @@
import * as fs from '@ccms/common/dist/fs'
import { PluginConfig, PluginConfigLoader } from './interfaces'
export class PluginFileConfig implements PluginConfig {
private loader: PluginConfigLoader
private file: string
constructor(loader: PluginConfigLoader, file: string, def = {}) {
this.loader = loader
this.file = file
if (fs.exists(file)) {
this.reload()
} else {
Object.assign(this, def)
}
this.initialize()
}
initialize() {
}
save() {
base.save(this.file, this.loader.dump(this))
}
reload() {
Object.assign(this, this.loader.load(base.read(this.file)))
}
}
export class PluginConfigFolder {
private loader: PluginConfigLoader
private folder: string
private configCache = new Map<string, PluginFileConfig>()
constructor(loader: PluginConfigLoader, folder: string) {
this.loader = loader
this.folder = folder
}
createConfig(path: string, def = {}) {
return new PluginFileConfig(this.loader, path, def)
}
getConfig(name: string, def = {}) {
let path = fs.concat(this.folder, name)
if (!this.configCache.has(path)) {
this.configCache.set(path, this.createConfig(path, def))
}
return this.configCache.get(path)
}
clear() {
this.configCache.clear()
}
save() {
this.configCache.forEach((config) => config.save())
}
reload() {
this.configCache.forEach((config) => config.reload())
}
}

View File

@@ -0,0 +1,18 @@
export const PluginConfigLoader = Symbol.for('PluginConfigLoader')
export interface PluginConfigLoader {
type: string
load(content: string): any
dump(variable: any): string
}
export interface PluginConfig {
/**
* Save Config to File
*/
readonly save?: () => void
/**
* Reload Config from File
*/
readonly reload?: () => void
[key: string]: any
}

View File

@@ -0,0 +1,2 @@
export * from './json-loader'
export * from './yaml-loader'

View File

@@ -0,0 +1,18 @@
import { provideSingletonNamed } from '@ccms/container'
import { PluginConfigLoader } from '../interfaces'
const LOADER_TYPE_NAME = 'json'
@provideSingletonNamed(PluginConfigLoader, LOADER_TYPE_NAME)
export class JsonPluginConfig implements PluginConfigLoader {
type: string = LOADER_TYPE_NAME
load(content: string) {
return JSON.parse(content)
}
dump(variable: any): string {
return JSON.stringify(variable, undefined, 4)
}
}

View File

@@ -0,0 +1,19 @@
import * as yaml from 'js-yaml'
import { provideSingletonNamed } from '@ccms/container'
import { PluginConfigLoader } from '../interfaces'
const LOADER_TYPE_NAME = 'yml'
@provideSingletonNamed(PluginConfigLoader, LOADER_TYPE_NAME)
export class YamlPluginConfig implements PluginConfigLoader {
type: string = LOADER_TYPE_NAME
load(content: string) {
return yaml.load(content)
}
dump(variable: any): string {
return yaml.dump(variable, { skipInvalid: true, lineWidth: 120 })
}
}

View File

@@ -5,6 +5,7 @@ export const METADATA_KEY = {
tab: Symbol.for("@ccms/plugin:tab"), tab: Symbol.for("@ccms/plugin:tab"),
listener: Symbol.for("@ccms/plugin:listener"), listener: Symbol.for("@ccms/plugin:listener"),
config: Symbol.for("@ccms/plugin:config"), config: Symbol.for("@ccms/plugin:config"),
playerdata: Symbol.for("@ccms/plugin:playerdata"),
stage: { stage: {
load: Symbol.for("@ccms/plugin:stage:load"), load: Symbol.for("@ccms/plugin:stage:load"),
enable: Symbol.for("@ccms/plugin:stage:enable"), enable: Symbol.for("@ccms/plugin:stage:enable"),

View File

@@ -2,7 +2,7 @@ import { plugin as pluginApi } from "@ccms/api"
import { injectable, decorate } from "@ccms/container" import { injectable, decorate } from "@ccms/container"
import { interfaces } from './interfaces' import { interfaces } from './interfaces'
import { METADATA_KEY } from './constants' import { METADATA_KEY } from './constants'
import { getPluginMetadatas, getPluginCommandMetadata, getPluginListenerMetadata, getPluginTabCompleterMetadata, getPluginConfigMetadata, getPluginStageMetadata, getPluginSources } from './utils' import { getPluginMetadatas, getPluginCommandMetadata, getPluginListenerMetadata, getPluginTabCompleterMetadata, getPluginConfigMetadata, getPluginStageMetadata, getPluginSources, getPluginPlayerDataMetadata } from './utils'
/** /**
* MiaoScript plugin * MiaoScript plugin
@@ -76,6 +76,7 @@ export function config(metadata: interfaces.ConfigMetadata = {}) {
metadata.variable = key metadata.variable = key
metadata.version = metadata.version ?? 1 metadata.version = metadata.version ?? 1
metadata.format = metadata.format ?? 'yml' metadata.format = metadata.format ?? 'yml'
metadata.migrate = metadata.migrate ?? true
metadata.autosave = metadata.autosave ?? false metadata.autosave = metadata.autosave ?? false
metadata.filename = metadata.filename ?? metadata.name + '.' + metadata.format metadata.filename = metadata.filename ?? metadata.name + '.' + metadata.format
let previousMetadata = getPluginConfigMetadata(target) let previousMetadata = getPluginConfigMetadata(target)
@@ -84,6 +85,21 @@ export function config(metadata: interfaces.ConfigMetadata = {}) {
} }
} }
export function playerdata(metadata: interfaces.PlayerDataMetadata = {}) {
return function (target: any, key: string) {
metadata.name = metadata.name || key
metadata.variable = key
metadata.version = metadata.version ?? 1
metadata.format = metadata.format ?? 'yml'
metadata.autosave = metadata.autosave ?? false
metadata.filename = metadata.filename ?? "username"
metadata.dir = metadata.dir ?? "playerdata"
let previousMetadata = getPluginPlayerDataMetadata(target)
previousMetadata.set(metadata.name, metadata)
Reflect.defineMetadata(METADATA_KEY.playerdata, previousMetadata, target.constructor)
}
}
function stage(stage: string) { function stage(stage: string) {
return (metadata: interfaces.ExecMetadata = {}) => { return (metadata: interfaces.ExecMetadata = {}) => {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {

View File

@@ -5,9 +5,13 @@ import { getPluginListenerMetadata } from './utils'
@provideSingleton(PluginEventManager) @provideSingleton(PluginEventManager)
export class PluginEventManager { export class PluginEventManager {
@Autowired() @Autowired()
private EventManager: event.Event private eventManager: event.Event
@Autowired() @Autowired()
private ServerChecker: server.ServerChecker private serverChecker: server.ServerChecker
@Autowired()
private nativePluginChecker: server.NativePluginChecker
private listenerMap = [];
constructor() { constructor() {
process.on('plugin.before.enable', this.registryListener.bind(this)) process.on('plugin.before.enable', this.registryListener.bind(this))
@@ -15,26 +19,44 @@ export class PluginEventManager {
} }
mapEventName() { mapEventName() {
return this.EventManager.mapEventName().toFixed(0) return this.eventManager.mapEventName().toFixed(0)
} }
private registryListener(pluginInstance: plugin.Plugin) { public registryListener(pluginInstance: plugin.Plugin, listener: any = pluginInstance) {
let events = getPluginListenerMetadata(pluginInstance) let events = getPluginListenerMetadata(listener)
let execes = []
for (const event of events) { for (const event of events) {
// ignore space listener // ignore space listener
if (!this.ServerChecker.check(event.servers)) { if (!this.serverChecker.check(event.servers)) {
console.debug(`[${pluginInstance.description.name}] ${event.target.constructor.name} incompatible event ${event.name} server(${event.servers}) ignore.`) console.debug(`[${pluginInstance.description.name}] ${event.target.constructor.name} incompatible server(${event.servers}) ignore event ${event.name}.`)
continue
}
// ignore space listener
if (!this.nativePluginChecker.check(event.plugins)) {
console.debug(`[${pluginInstance.description.name}] ${event.target.constructor.name} require native plugins(${event.plugins}) ignore event ${event.name}.`)
continue continue
} }
// here must bind this to pluginInstance // here must bind this to pluginInstance
let exec = event.target[event.executor] let exec = event.target[event.executor]
let execBinded = exec.bind(pluginInstance) let execBinded = exec.bind(listener)
execBinded.executor = event.executor execBinded.executor = event.executor
exec.off = this.EventManager.listen(pluginInstance, event.name, execBinded, event.priority, event.ignoreCancel) exec.off = this.eventManager.listen(pluginInstance, event.name, execBinded, event.priority, event.ignoreCancel)
execes.push(exec)
} }
let off = () => {
if (off['offed']) return
off['offed'] = true
execes.forEach((exec: { off: () => void }) => exec.off())
}
listener.off = off
} }
private unregistryListener(pluginInstance: plugin.Plugin) { private unregistryListener(pluginInstance: plugin.Plugin, listener: any = pluginInstance) {
this.EventManager.disable(pluginInstance) if (listener && listener.off) {
listener.off()
}
if (pluginInstance) {
this.eventManager.disable(pluginInstance)
}
} }
} }

View File

@@ -1,16 +1,25 @@
import './scanner/file-scanner' import '@ccms/verify'
import './scanner/js-scanner'
import './loader/ioc-loader' import './loader/ioc-loader'
import './loader/basic-loader' import './loader/basic-loader'
export * from './config' export * from './config'
export * from './config/config'
export * from './manager' export * from './manager'
export * from './decorators' export * from './decorators'
export * from './interfaces' export * from './interfaces'
export * from './event'
export * from './command'
export { export {
plugin as JSPlugin, plugin as JSPlugin,
cmd as Cmd, cmd as Cmd,
tab as Tab, tab as Tab,
listener as Listener, listener as Listener,
config as Config config as Config,
playerdata as PlayerData
} from './decorators' } from './decorators'
import '@ccms/database'

View File

@@ -8,7 +8,7 @@ export namespace interfaces {
@injectable() @injectable()
export abstract class Plugin implements plugin.Plugin { export abstract class Plugin implements plugin.Plugin {
public description: plugin.PluginMetadata public description: plugin.PluginMetadata
public logger: Console public logger: MiaoScriptConsole
@inject(server.Console) @inject(server.Console)
private Console: MiaoScriptConsole private Console: MiaoScriptConsole
@@ -28,6 +28,11 @@ export namespace interfaces {
return dataFolder.getAbsolutePath() return dataFolder.getAbsolutePath()
} }
public registryCommand(executor: any) { }
public unregistryCommand(executor: any) { }
public registryListener(listener: any) { }
public unregistryListener(listener: any) { }
public load() { } public load() { }
public enable() { } public enable() { }
public disable() { } public disable() { }
@@ -59,6 +64,10 @@ export namespace interfaces {
* 自动化主命令 * 自动化主命令
*/ */
autoMain?: boolean autoMain?: boolean
/**
* 子命令权限效验
*/
permission?: boolean | string
} }
export interface ListenerMetadata extends ExecMetadata { export interface ListenerMetadata extends ExecMetadata {
/** /**
@@ -69,8 +78,46 @@ export namespace interfaces {
* 是否忽略已取消的事件 * 是否忽略已取消的事件
*/ */
ignoreCancel?: boolean ignoreCancel?: boolean
/**
* 依赖插件 没有就不加载
*/
plugins?: string[]
} }
export interface ConfigMetadata extends plugin.BaseMetadata { export interface ConfigMetadata extends plugin.BaseMetadata {
/**
* 配置文件版本号
*/
version?: number
/**
* 默认配置
*/
default?: any
/**
* 实体变量名称
*/
variable?: string
/**
* 配置文件格式 默认 yml
*/
format?: string
/**
* 是否合并默认配置
*/
migrate?: boolean
/**
* 自动保存 默认为 false
*/
autosave?: boolean
/**
* 配置文件名称
*/
filename?: string
/**
* 配置文件全路径
*/
file?: any
}
export interface PlayerDataMetadata extends plugin.BaseMetadata {
/** /**
* 配置文件版本号 * 配置文件版本号
*/ */
@@ -92,12 +139,16 @@ export namespace interfaces {
*/ */
autosave?: boolean autosave?: boolean
/** /**
* 配置文件名称 * 配置文件名称 默认玩家名称
*/ */
filename?: string filename?: "username" | "uuid"
/** /**
* 配置文件全路径 * 配置文件目录 默认 playerdata
*/ */
file?: any dir?: string
/**
* 配置文件子目录 默认为空
*/
subdir?: string
} }
} }

View File

@@ -63,13 +63,13 @@ export class IocLoader implements plugin.PluginLoader {
} }
private bindPlugin(metadata: plugin.PluginMetadata) { private bindPlugin(metadata: plugin.PluginMetadata) {
try { if (this.container.isBoundNamed(plugin.Plugin, metadata.name)) {
let pluginInstance = this.container.getNamed<plugin.Plugin>(plugin.Plugin, metadata.name) let pluginInstance = this.container.getNamed<plugin.Plugin>(plugin.Plugin, metadata.name)
if (pluginInstance.description.source + '' !== metadata.source + '') { if (pluginInstance.description.source + '' !== metadata.source + '') {
console.i18n('ms.plugin.manager.build.duplicate', { exists: pluginInstance.description.source, source: metadata.source }) console.i18n('ms.plugin.manager.build.duplicate', { exists: pluginInstance.description.source, source: metadata.source })
} }
this.container.rebind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name) this.container.rebind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name)
} catch { } else {
this.container.bind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name) this.container.bind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name)
} }
} }

View File

@@ -2,12 +2,11 @@ import i18n from '@ccms/i18n'
import { plugin, server } from '@ccms/api' import { plugin, server } from '@ccms/api'
import { provideSingleton, Container, ContainerInstance, Autowired } from '@ccms/container' import { provideSingleton, Container, ContainerInstance, Autowired } from '@ccms/container'
import './config'
import { interfaces } from './interfaces' import { interfaces } from './interfaces'
import { PluginTaskManager } from './task' import { PluginTaskManager } from './task'
import { PluginEventManager } from './event' import { PluginEventManager } from './event'
import { PluginCommandManager } from './command'
import { PluginConfigManager } from './config' import { PluginConfigManager } from './config'
import { PluginCommandManager } from './command'
const Thread = Java.type('java.lang.Thread') const Thread = Java.type('java.lang.Thread')
@@ -64,8 +63,13 @@ export class PluginManagerImpl implements plugin.PluginManager {
initialize() { initialize() {
if (this.pluginInstance === undefined) { throw new Error("Can't found Plugin Instance!") } if (this.pluginInstance === undefined) { throw new Error("Can't found Plugin Instance!") }
if (this.initialized !== true) { if (this.initialized !== true) {
process.emit('plugin.manager.before.initialize', this)
console.i18n('ms.plugin.initialize', { plugin: this.pluginInstance, loader: Thread.currentThread().contextClassLoader }) console.i18n('ms.plugin.initialize', { plugin: this.pluginInstance, loader: Thread.currentThread().contextClassLoader })
console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) try {
console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType })
} catch (error) {
console.i18n('ms.plugin.event.map.error', { type: this.serverType, error })
}
let pluginScanner = this.container.getAll<plugin.PluginScanner>(plugin.PluginScanner) let pluginScanner = this.container.getAll<plugin.PluginScanner>(plugin.PluginScanner)
pluginScanner.forEach((scanner) => { pluginScanner.forEach((scanner) => {
console.debug(`loading plugin sacnner ${scanner.type}...`) console.debug(`loading plugin sacnner ${scanner.type}...`)
@@ -77,37 +81,38 @@ export class PluginManagerImpl implements plugin.PluginManager {
this.loaderMap.set(loader.type, loader) this.loaderMap.set(loader.type, loader)
}) })
this.initialized = true this.initialized = true
process.emit('plugin.initialize') process.emit('plugin.manager.after.initialize', this)
} }
} }
scan(folder: string): void { scan(folder: string): void {
if (!folder) { throw new Error('plugin scan folder can\'t be empty!') } if (!folder) { throw new Error('plugin scan folder can\'t be empty!') }
this.initialize() this.initialize()
process.emit('plugin.manager.before.scan', folder, this)
for (const [, scanner] of this.sacnnerMap) { for (const [, scanner] of this.sacnnerMap) {
try { try {
console.i18n('ms.plugin.manager.scan', { scanner: scanner.type, folder }) console.i18n('ms.plugin.manager.scan', { scanner: scanner.type, folder })
let plugins = scanner.scan(folder) let loadMetadatas = scanner.scan(folder)
console.i18n('ms.plugin.manager.scan.finish', { scanner: scanner.type, folder, size: plugins.length }) console.i18n('ms.plugin.manager.scan.finish', { scanner: scanner.type, folder, size: loadMetadatas.length })
plugins.forEach(loadMetadata => { for (const loadMetadata of loadMetadatas) {
try { try {
this.loadAndRequirePlugin(loadMetadata) this.loadAndRequirePlugin(loadMetadata)
} catch (error: any) { } catch (error: any) {
console.error(`plugin scanner ${scanner.type} load ${loadMetadata.file} occurred error ${error}`) console.console(`§c扫描器 §4${scanner.type} §c文件 §4${loadMetadata.file.toString().replace(root, '')} §c编译失败 请提供下列错误给开发者`)
console.ex(error) console.ex(error)
} }
}) }
} catch (error: any) { } catch (error: any) {
console.error(`plugin scanner ${scanner.type} occurred error ${error}`)
console.ex(error) console.ex(error)
} }
} }
process.emit('plugin.scan', folder) process.emit('plugin.manager.after.scan', folder, this)
} }
build(): void { build(): void {
process.emit('plugin.manager.before.build', this)
this.buildPlugins() this.buildPlugins()
process.emit('plugin.build') process.emit('plugin.manager.after.build', this)
} }
private logStage(plugin: plugin.Plugin, stage: string) { private logStage(plugin: plugin.Plugin, stage: string) {
@@ -132,6 +137,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) { private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) {
if (!loadMetadata) { throw new Error('loadMetadata can\'t be undefiend when loadPlugin!') } if (!loadMetadata) { throw new Error('loadMetadata can\'t be undefiend when loadPlugin!') }
if (loadMetadata.loaded) { throw new Error(`Plugin file ${loadMetadata.file} is already loaded by ${loadMetadata.loader?.type}!`) } if (loadMetadata.loaded) { throw new Error(`Plugin file ${loadMetadata.file} is already loaded by ${loadMetadata.loader?.type}!`) }
process.emit(`plugin.before.require`, loadMetadata)
try { try {
for (const [, loader] of this.loaderMap) { for (const [, loader] of this.loaderMap) {
if (this.loaderRequirePlugin(loadMetadata, loader)?.loaded) return loadMetadata.metadata if (this.loaderRequirePlugin(loadMetadata, loader)?.loaded) return loadMetadata.metadata
@@ -140,7 +146,6 @@ export class PluginManagerImpl implements plugin.PluginManager {
console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error }) console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error })
console.ex(error) console.ex(error)
} }
console.console(`§6scanner: §b${loadMetadata.scanner.type} §ccan\'t load §6file §b${loadMetadata.file}. §eskip!`)
} }
private loaderRequirePlugin(loadMetadata: plugin.PluginLoadMetadata, loader: plugin.PluginLoader) { private loaderRequirePlugin(loadMetadata: plugin.PluginLoadMetadata, loader: plugin.PluginLoader) {
@@ -154,6 +159,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
} }
this.metadataMap.set(metadata.name, metadata) this.metadataMap.set(metadata.name, metadata)
metadata.loadMetadata = loadMetadata metadata.loadMetadata = loadMetadata
process.emit(`plugin.after.require`, loadMetadata)
} }
return loadMetadata return loadMetadata
} catch (error: any) { } catch (error: any) {
@@ -169,6 +175,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
private loadAndRequirePlugin(loadMetadata: plugin.PluginLoadMetadata) { private loadAndRequirePlugin(loadMetadata: plugin.PluginLoadMetadata) {
let startTime = Date.now() let startTime = Date.now()
let metadata = this.loadPlugin(loadMetadata.scanner.load(loadMetadata)) let metadata = this.loadPlugin(loadMetadata.scanner.load(loadMetadata))
if (!metadata) { throw new Error('load plugin metadata failed.') }
console.i18n('ms.plugin.manager.build', { console.i18n('ms.plugin.manager.build', {
name: loadMetadata.metadata.name, name: loadMetadata.metadata.name,
version: loadMetadata.metadata.version, version: loadMetadata.metadata.version,
@@ -184,11 +191,14 @@ export class PluginManagerImpl implements plugin.PluginManager {
* 从文件加载插件 * 从文件加载插件
* @param file java.io.File * @param file java.io.File
*/ */
loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin { loadFromFile(file: string, ext: any = 'js'): plugin.Plugin {
if (!file) { throw new Error('plugin file can\'t be undefiend!') } if (!file) { throw new Error('plugin file can\'t be undefiend.') }
if (!scanner) { throw new Error('plugin scanner can\'t be undefiend!') } let scanner = this.sacnnerMap.get(ext)
if (!scanner) { throw new Error(`plugin scanner ${ext} can't found in sacnnerMap.`) }
let metadata = this.loadAndRequirePlugin(scanner.read(file)) let metadata = this.loadAndRequirePlugin(scanner.read(file))
let plugin = this.buildPlugin(metadata) this.buildPlugin(metadata)
let plugin = metadata.target
if (!plugin) { throw new Error(`plugin scanner ${ext} can't found in sacnnerMap.`) }
this.load(plugin) this.load(plugin)
this.enable(plugin) this.enable(plugin)
return plugin return plugin
@@ -213,12 +223,20 @@ export class PluginManagerImpl implements plugin.PluginManager {
reload(...args: any[]): void { reload(...args: any[]): void {
this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => {
this.disable(pl) this.disable(pl)
this.loadFromFile(pl.description.loadMetadata.file, pl.description.loadMetadata.scanner) this.loadFromFile(pl.description.loadMetadata.file, pl.description.loadMetadata.scanner.type)
}) })
} }
has(name: string) {
return this.instanceMap.has(name)
}
get(name: string) {
return this.instanceMap.get(name) || null
}
getPlugin(name: string) { getPlugin(name: string) {
return this.instanceMap.get(name) return this.instanceMap.get(name) || null
} }
getPlugins() { getPlugins() {
@@ -235,12 +253,12 @@ export class PluginManagerImpl implements plugin.PluginManager {
} }
private checkAndGet(name: string | plugin.Plugin | undefined | any): Map<string, plugin.Plugin> | plugin.Plugin[] { private checkAndGet(name: string | plugin.Plugin | undefined | any): Map<string, plugin.Plugin> | plugin.Plugin[] {
if (name === undefined) throw new Error(`checkAndGet Plugin can't be undefiend!`) if (name === undefined) throw new Error(`checkAndGet Plugin can't be undefiend.`)
if (name == this.instanceMap) { return this.instanceMap } if (name == this.instanceMap) { return this.instanceMap }
if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] } if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] }
if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] } if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] }
if (name.description?.name) { return [name as plugin.Plugin] } if (name.description?.name) { return [name as plugin.Plugin] }
throw new Error(`Plugin ${JSON.stringify(name)} not exist!`) throw new Error(`Plugin ${JSON.stringify(name)} not exist.`)
} }
private buildPlugins() { private buildPlugins() {
@@ -248,10 +266,10 @@ export class PluginManagerImpl implements plugin.PluginManager {
if (metadata?.depends?.length) { if (metadata?.depends?.length) {
this.lazyMetadataMap.set(key, metadata) this.lazyMetadataMap.set(key, metadata)
} else { } else {
this.buildPlugin(metadata) this.tryBuildPlugin(metadata)
} }
}) })
this.lazyMetadataMap.forEach((metadata, key) => this.buildPlugin(metadata)) this.lazyMetadataMap.forEach((metadata, key) => this.tryBuildPlugin(metadata))
} }
private checkDepends(depends: string | string[]) { private checkDepends(depends: string | string[]) {
@@ -266,22 +284,29 @@ export class PluginManagerImpl implements plugin.PluginManager {
for (const depend of depends) { if (!this.nativePluginManager.has(depend)) loseDepends.push(depend) } for (const depend of depends) { if (!this.nativePluginManager.has(depend)) loseDepends.push(depend) }
return loseDepends return loseDepends
} }
private buildPlugin(metadata: plugin.PluginMetadata) {
private tryBuildPlugin(metadata: plugin.PluginMetadata) {
try { try {
if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) } return this.buildPlugin(metadata)
if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) }
if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) }
let loseDepends = this.checkDepends(metadata.depends) || []
if (loseDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查脚本依赖 §3[${loseDepends.join(',')}] §4是否安装完整!`) }
let loseNativeDepends = this.checkNativeDepends(metadata.nativeDepends) || []
if (loseNativeDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查插件依赖 §3[${loseNativeDepends.join(',')}] §4是否安装完整!`) }
let pluginInstance = this.loaderMap.get(metadata.type).build(metadata)
if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) }
this.instanceMap.set(metadata.name, pluginInstance)
return pluginInstance
} catch (error: any) { } catch (error: any) {
console.console(`§4无法加载插件 §b${metadata.name} §4构建插件失败!`)
console.ex(error) console.ex(error)
} }
} }
private buildPlugin(metadata: plugin.PluginMetadata) {
process.emit(`plugin.before.build`, metadata)
if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) }
if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) }
if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) }
let loseDepends = this.checkDepends(metadata.depends) || []
if (loseDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查脚本依赖 §3[${loseDepends.join(',')}] §4是否安装完整!`) }
let loseNativeDepends = this.checkNativeDepends(metadata.nativeDepends) || []
if (loseNativeDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查插件依赖 §3[${loseNativeDepends.join(',')}] §4是否安装完整!`) }
let pluginInstance = this.loaderMap.get(metadata.type).build(metadata)
if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) }
metadata.target = pluginInstance
this.instanceMap.set(metadata.name, pluginInstance)
process.emit(`plugin.after.build`, metadata, pluginInstance)
return pluginInstance
}
} }

View File

@@ -2,7 +2,7 @@ import { plugin } from "@ccms/api"
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
import { provideSingletonNamed } from "@ccms/container" import { provideSingletonNamed } from "@ccms/container"
const SCANNER_TYPE_NAME = 'file' const SCANNER_TYPE_NAME = 'js'
@provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME) @provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME)
export class JSFileScanner implements plugin.PluginScanner { export class JSFileScanner implements plugin.PluginScanner {

View File

@@ -67,7 +67,13 @@ function getPluginConfigMetadata(target: any) {
) || new Map<string, interfaces.ConfigMetadata>() ) || new Map<string, interfaces.ConfigMetadata>()
return configMetadata return configMetadata
} }
function getPluginPlayerDataMetadata(target: any) {
let playerdataMetadata: Map<string, interfaces.ConfigMetadata> = Reflect.getMetadata(
METADATA_KEY.playerdata,
target.constructor
) || new Map<string, interfaces.ConfigMetadata>()
return playerdataMetadata
}
function getPluginStageMetadata(target: any, stage: string) { function getPluginStageMetadata(target: any, stage: string) {
let stageMetadata: interfaces.ExecMetadata[] = Reflect.getMetadata( let stageMetadata: interfaces.ExecMetadata[] = Reflect.getMetadata(
METADATA_KEY.stage[stage], METADATA_KEY.stage[stage],
@@ -86,5 +92,6 @@ export {
getPluginTabCompleterMetadata, getPluginTabCompleterMetadata,
getPluginListenerMetadata, getPluginListenerMetadata,
getPluginConfigMetadata, getPluginConfigMetadata,
getPluginPlayerDataMetadata,
getPluginStageMetadata getPluginStageMetadata
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/polyfill", "name": "@ccms/polyfill",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript polyfill package", "description": "MiaoScript polyfill package",
"author": "MiaoWoo <admin@yumc.pw>", "author": "MiaoWoo <admin@yumc.pw>",
"homepage": "https://github.com/circlecloud/ms.git", "homepage": "https://github.com/circlecloud/ms.git",
@@ -14,14 +14,14 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@ccms/i18n": "^0.18.0", "@ccms/i18n": "^0.28.0-beta.2",
"@ccms/nodejs": "^0.18.0", "@ccms/nodejs": "^0.28.0-beta.2",
"core-js": "^3.21.0" "core-js": "^3.27.2"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.18.0", "@ccms/nashorn": "^0.28.0-beta.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -12,5 +12,7 @@ import 'core-js'
process.on('exit', () => require.disable()) process.on('exit', () => require.disable())
global.setGlobal('Proxy', require('./proxy').Proxy) global.setGlobal('Proxy', require('./proxy').Proxy)
global.setGlobal('XMLHttpRequest', require('./xml-http-request').XMLHttpRequest) global.setGlobal('XMLHttpRequest', require('./xml-http-request').XMLHttpRequest)
global.setGlobal('Buffer', require('./buffer').Buffer)
global.setGlobal('Blob', require('blob-polyfill').Blob) global.setGlobal('Blob', require('blob-polyfill').Blob)
console.i18n("ms.polyfill.completed", { time: (new Date().getTime() - polyfillStartTime) / 1000 }) console.i18n("ms.polyfill.completed", { time: (new Date().getTime() - polyfillStartTime) / 1000 })
export default true

View File

@@ -2,6 +2,7 @@ import { EventEmitter } from 'events'
const System = Java.type('java.lang.System') const System = Java.type('java.lang.System')
const Thread = Java.type('java.lang.Thread') const Thread = Java.type('java.lang.Thread')
const ManagementFactory = Java.type('java.lang.management.ManagementFactory')
const InterruptedException = Java.type('java.lang.InterruptedException') const InterruptedException = Java.type('java.lang.InterruptedException')
const ThreadGroup = Java.type("java.lang.ThreadGroup") const ThreadGroup = Java.type("java.lang.ThreadGroup")
const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger")
@@ -15,19 +16,35 @@ const DelayQueue = Java.type('java.util.concurrent.DelayQueue')
const JavaScriptTask = Java.type(base.getJavaScriptTaskClass().name) const JavaScriptTask = Java.type(base.getJavaScriptTaskClass().name)
const threadCount = new AtomicInteger(0) const threadCount = new AtomicInteger(0)
const threadGroup = new ThreadGroup("@ccms/ployfill-micro-task") const threadGroup = new ThreadGroup("@ccms/micro-task")
const microTaskPool = new ThreadPoolExecutor( const microTaskPool = new ThreadPoolExecutor(
100, 200, 60, TimeUnit.SECONDS, 100, 200, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue(300), new LinkedBlockingQueue(1024),
new ThreadFactory((run: any) => new Thread(threadGroup, run, "@ccms/micro-task-" + threadCount.incrementAndGet())) new ThreadFactory((run: any) => new Thread(threadGroup, run, "@ccms/micro-task-" + threadCount.incrementAndGet()))
) )
class Process extends EventEmitter { class Process extends EventEmitter {
readonly version = base.version
readonly versions = []
readonly config = {}
readonly pid: number = parseInt(ManagementFactory.getRuntimeMXBean().getName().split('@')[0])
readonly ppid: number
title: string
readonly arch: string = System.getProperty("os.arch")
readonly platform = System.getProperty("os.name")
env = { env = {
__noSuchProperty__: (prop) => { __noSuchProperty__: (prop) => {
return System.getenv(prop) return System.getenv(prop)
} }
} }
platform = System.getProperty("os.name")
stdout = System.out
stderr = System.err
stdin = System.in
execArgv = ''
execPath = ''
constructor() { constructor() {
super() super()
this.on('exit', () => { this.on('exit', () => {
@@ -49,10 +66,10 @@ class Process extends EventEmitter {
} }
}) })
} }
nextTick(func: Function, ...args: any[]) { nextTick(callback: Function, ...args: any[]): void {
microTaskPool.execute(() => { microTaskPool.execute(() => {
try { try {
func(args) callback(args)
} catch (origin: any) { } catch (origin: any) {
try { try {
super.emit('error', origin) super.emit('error', origin)
@@ -65,27 +82,63 @@ class Process extends EventEmitter {
} }
exit(code: number) { exit(code: number) {
console.log(`process exit by code ${code}!`) console.log(`process exit by code ${code}!`)
this.emit('exit', code) this.emit('exit', this.exitCode = code)
}
exitCode = 0
openStdin() {
throw new Error('MiaoScript unsupport openStdin.')
}
chdir(directory: string): void {
console.error('MiaoScript unsupport chdir. lock at ' + root)
}
cwd() {
return root
}
getgid(): number {
throw new Error('MiaoScript unsupport getgid.')
}
setgid(id: number | string) {
throw new Error('MiaoScript unsupport setgid.')
}
getuid(): number {
throw new Error('MiaoScript unsupport getuid.')
}
setuid(id: number | string) {
throw new Error('MiaoScript unsupport setuid.')
}
setUncaughtExceptionCaptureCallback(cb: ((err: Error) => void) | null) {
if (cb == null) {
this.removeAllListeners('error')
} else {
this.on('error', cb)
}
}
hasUncaughtExceptionCaptureCallback() {
return this.listenerCount('error') > 0
}
kill(pid: number, signal?: string | number): true {
throw new Error('MiaoScript unsupport kill.')
return true
} }
toString() { toString() {
return "[object process]" return "[object process]"
} }
} }
class EventLoop { class EventLoop {
private threadCount = new AtomicInteger(0)
private eventLoopMainThread = undefined private eventLoopMainThread = undefined
private eventLoopTaskQueue = new DelayQueue() private eventLoopTaskQueue = new DelayQueue()
private taskExecTimeout = 3 private taskExecuteTimeout = 3000
private fixedThreadPool = undefined private fixedThreadPool = undefined
constructor() { constructor() {
this.taskExecTimeout = parseInt(process.env.MS_NODE_EVENT_LOOP_TIMEOUT) || 3 this.taskExecuteTimeout = parseInt(process.env.MS_TASK_EXECUTE_TIMEOUT) || 3000
this.fixedThreadPool = new ThreadPoolExecutor( this.fixedThreadPool = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS, 8, 16, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue(300), new LinkedBlockingQueue(1024),
new ThreadFactory((run: any) => { new ThreadFactory((run: any) => {
let thread = new Thread(run, "@ccms/node-shim/event-loop-exec") let thread = new Thread(run, "@ccms/event-loop-" + this.threadCount.incrementAndGet())
thread.setDaemon(true) thread.setDaemon(true)
return thread return thread
})) }))
@@ -115,7 +168,7 @@ class EventLoop {
this.intervalTasks = undefined this.intervalTasks = undefined
this.eventLoopMainThread = undefined this.eventLoopMainThread = undefined
} }
}, "@ccms/node-shim/event-loop") }, "@ccms/event-loop")
this.eventLoopMainThread.setDaemon(true) this.eventLoopMainThread.setDaemon(true)
process.on('exit', () => { process.on('exit', () => {
this.eventLoopMainThread.interrupt() this.eventLoopMainThread.interrupt()
@@ -137,14 +190,16 @@ class EventLoop {
if (!callback) { if (!callback) {
throw new Error(`task ${name} callback function can't be null!`) throw new Error(`task ${name} callback function can't be null!`)
} }
if (this.fixedThreadPool.isShutdown()) { return console.warn(`FixedThreadPool isTerminated! ignore Task ${name}!`) } if (this.fixedThreadPool.isShutdown()) {
return console.warn(`FixedThreadPool isTerminated! ignore Task ${name}!`)
}
try { try {
this.fixedThreadPool.submit(new Callable({ this.fixedThreadPool.submit(new Callable({
call: () => { call: () => {
try { try {
callback.apply(undefined, args) callback.apply(undefined, args)
} catch (cause: any) { } catch (cause: any) {
cause = cause.getCause && cause.getCause() || cause cause = cause.getCause ? cause.getCause() : cause
try { try {
process.emit('error', cause) process.emit('error', cause)
} catch (error: any) { } catch (error: any) {
@@ -153,13 +208,13 @@ class EventLoop {
} }
} }
} }
})).get(this.taskExecTimeout, TimeUnit.SECONDS) })).get(this.taskExecuteTimeout, TimeUnit.MILLISECONDS)
} catch (error: any) { } catch (error: any) {
if (error instanceof InterruptedException) { if (error instanceof InterruptedException) {
return console.warn(`FixedThreadPool isInterrupted exit! Task ${name} exec exit!`) return console.warn(`FixedThreadPool isInterrupted exit! Task ${name} exec exit!`)
} }
if (error instanceof TimeoutException) { if (error instanceof TimeoutException) {
return console.warn(`Task ${name} => ${callback} exec time greater than ${this.taskExecTimeout}s!`) return console.warn(`Task ${name} => ${callback} exec time greater than ${this.taskExecuteTimeout}ms!`)
} }
throw error.getCause && error.getCause() || error throw error.getCause && error.getCause() || error
} }

View File

@@ -68,13 +68,14 @@ type EventType =
| 'timeout' | 'timeout'
| 'loadend' | 'loadend'
| 'loadstart' | 'loadstart'
type HttpHeader = { [key: string]: string } type RequestHttpHeader = { [key: string]: string }
type HttpHeader = { [key: string]: string[] }
const executor = Executors.newCachedThreadPool() const executor = Executors.newCachedThreadPool()
export class XMLHttpRequest { export class XMLHttpRequest {
private _timeout: number = 120000; private _connectTimeout: number = 5000;
private _readTimeout: number = 120000;
private _responseType: ResponseType = 'text'; private _responseType: ResponseType = 'text';
private _withCredentials: boolean private _withCredentials: boolean
@@ -84,7 +85,7 @@ export class XMLHttpRequest {
private _url: string private _url: string
private _async: boolean private _async: boolean
private _mimeType: string private _mimeType: string
private _requestHeaders: HttpHeader = {}; private _requestHeaders: RequestHttpHeader = {};
private _status: number = 0; private _status: number = 0;
private _statusText: string = null; private _statusText: string = null;
@@ -96,10 +97,22 @@ export class XMLHttpRequest {
private _connection = null; private _connection = null;
get timeout() { get timeout() {
return this._timeout return this._readTimeout
} }
set timeout(timeout: number) { set timeout(timeout: number) {
this._timeout = timeout this._readTimeout = timeout
}
get connectTimeout() {
return this._connectTimeout
}
set connectTimeout(timeout: number) {
this._connectTimeout = timeout
}
get readTimeout() {
return this._readTimeout
}
set readTimeout(timeout: number) {
this._readTimeout = timeout
} }
get readyState() { get readyState() {
return this._readyState return this._readyState
@@ -143,7 +156,7 @@ export class XMLHttpRequest {
this._requestHeaders[key] = val this._requestHeaders[key] = val
} }
getResponseHeader(key: string): string { getResponseHeader(key: string): string {
return this._responseHeaders[key] return this._responseHeaders[key]?.[0]
} }
getAllResponseHeaders(): any { getAllResponseHeaders(): any {
return this._responseHeaders return this._responseHeaders
@@ -169,8 +182,8 @@ export class XMLHttpRequest {
this._connection.setRequestMethod(this._method) this._connection.setRequestMethod(this._method)
this._connection.setDoOutput(true) this._connection.setDoOutput(true)
this._connection.setDoInput(true) this._connection.setDoInput(true)
this._connection.setConnectTimeout(this._timeout) this._connection.setConnectTimeout(this._connectTimeout)
this._connection.setReadTimeout(this._timeout) this._connection.setReadTimeout(this._readTimeout)
this.setRequestHeader('X-Requested-With', 'XMLHttpRequest') this.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
this.setReadyState(ReadyState.OPENED) this.setReadyState(ReadyState.OPENED)
@@ -181,7 +194,7 @@ export class XMLHttpRequest {
} }
if (this._readyState !== ReadyState.OPENED) { throw new Error(`Error Status ${this._readyState}!`) } if (this._readyState !== ReadyState.OPENED) { throw new Error(`Error Status ${this._readyState}!`) }
let future = executor.submit(new Callable({ call: () => this._send(body) })) let future = executor.submit(new Callable({ call: () => this._send(body) }))
if (!this._async) { future.get(this._timeout, TimeUnit.MILLISECONDS) } if (!this._async) { future.get(this._connectTimeout + this._readTimeout + 100, TimeUnit.MILLISECONDS) }
return future return future
} }
get() { get() {
@@ -199,22 +212,23 @@ export class XMLHttpRequest {
} }
abort() { abort() {
this._connection.disconnect() this._connection.disconnect()
this.onabort && this.onabort() this.onabort?.()
} }
private _send(body?: string | object) { private _send(body?: string | object) {
try { try {
this._connection.connect() this._connection.connect()
this.onloadstart && this.onloadstart() this.onloadstart?.()
if (body) { if (body) {
let bodyType = Object.prototype.toString.call(body) let bodyType = Object.prototype.toString.call(body)
if (typeof body !== "string") { throw new Error(`body(${bodyType}) must be string!`) } if (typeof body !== "string") { throw new Error(`body(${bodyType}) must be string.`) }
var out = this._connection.getOutputStream() var out = this._connection.getOutputStream()
out.write(new JavaString(body).getBytes(UTF_8)) out.write(new JavaString(body).getBytes(UTF_8))
out.flush() out.flush()
out.close() out.close()
} }
this.setReadyState(ReadyState.LOADING) this.setReadyState(ReadyState.LOADING)
this.onload?.()
this._status = this._connection.getResponseCode() this._status = this._connection.getResponseCode()
this._statusText = this._connection.getResponseMessage() this._statusText = this._connection.getResponseMessage()
if (this._status >= 0 && this._status < 300) { if (this._status >= 0 && this._status < 300) {
@@ -224,8 +238,8 @@ export class XMLHttpRequest {
} else { } else {
this._responseText = this.readOutput(this._connection.getErrorStream()) this._responseText = this.readOutput(this._connection.getErrorStream())
} }
this.setResponseHeaders(this._connection.getHeaderFields()) this.setResponseHeaders()
this.onloadend && this.onloadend() this.onloadend?.()
} catch (ex: any) { } catch (ex: any) {
if (ex instanceof SocketTimeoutException && this.ontimeout) { if (ex instanceof SocketTimeoutException && this.ontimeout) {
return this.ontimeout(ex) return this.ontimeout(ex)
@@ -239,15 +253,15 @@ export class XMLHttpRequest {
} }
} }
private setResponseHeaders(header: any) { private setResponseHeaders() {
header.forEach((key: string | number, value: string | any[]) => { this._connection.getHeaderFields().forEach((key: string | number, value: any[]) => {
this._responseHeaders[key + ''] = value[value.length - 1] + '' this._responseHeaders[key + ''] = Java.from(value)
}) })
} }
private setReadyState(state: ReadyState) { private setReadyState(state: ReadyState) {
this._readyState = state this._readyState = state
this.onreadystatechange && this.onreadystatechange() this.onreadystatechange?.()
} }
private readOutput(input: any) { private readOutput(input: any) {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/protocol", "name": "@ccms/protocol",
"version": "0.18.0", "version": "0.28.0-beta.2",
"description": "MiaoScript protocol package", "description": "MiaoScript protocol package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -21,7 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.5.5" "typescript": "^4.9.5"
} }
} }

View File

@@ -0,0 +1,26 @@
{
"name": "@ccms/qrcode",
"version": "0.28.0-beta.2",
"description": "MiaoScript qrcode package",
"keywords": [
"miaoscript",
"minecraft",
"bukkit",
"sponge"
],
"author": "MiaoWoo <admin@yumc.pw>",
"homepage": "https://github.com/circlecloud/ms.git",
"license": "ISC",
"main": "dist/index.js",
"scripts": {
"clean": "rimraf dist",
"watch": "tsc --watch",
"build": "yarn clean && tsc",
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"reflect-metadata": "^0.1.13",
"rimraf": "^4.1.2",
"typescript": "^4.9.5"
}
}

1243
packages/qrcode/src/index.ts Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More