Compare commits

...

40 Commits

Author SHA1 Message Date
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
123 changed files with 15778 additions and 6134 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,17 +1,16 @@
{ {
"version": "0.21.0", "version": "0.27.6",
"useWorkspaces": true, "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

@@ -12,14 +12,16 @@
"build": "lerna run build", "build": "lerna run build",
"ug": "yarn upgrade-interactive --latest", "ug": "yarn upgrade-interactive --latest",
"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 --verify-access --force-publish",
"lpb": "lerna publish --registry https://registry.npmjs.org --canary --pre-dist-tag beta" "lpb": "lerna publish --preid beta --dist-tag beta --verify-access --force-publish",
"lpc": "lerna publish --canary --preid beta --pre-dist-tag beta --verify-access --force-publish",
"lpf": "lerna publish from-package --yes"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"devDependencies": { "devDependencies": {
"lerna": "^4.0.0" "lerna": "^6.4.1"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/amqp", "name": "@ccms/amqp",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.21.0", "@ccms/nashorn": "^0.27.6",
"@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.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/api", "name": "@ccms/api",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0", "@ccms/container": "^0.27.6",
"@ccms/polyfill": "^0.21.0", "@ccms/polyfill": "^0.27.6",
"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.6.3" "typescript": "^4.9.5"
} }
} }

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

@@ -6,6 +6,10 @@ export namespace database {
* 数据库配置 * 数据库配置
*/ */
export interface DataBaseConfig { export interface DataBaseConfig {
/**
* 数据库类型
*/
type: 'h2' | 'mysql' | 'mongodb' | 'sqlite' | 'postgres' | 'redis'
/** /**
* 数据库连接串 * 数据库连接串
*/ */
@@ -26,6 +30,10 @@ export namespace database {
* 链接属性 * 链接属性
*/ */
properties?: { [key: string]: any } properties?: { [key: string]: any }
/**
* 调试模式
*/
debug?: boolean
} }
@injectable() @injectable()
@@ -35,6 +43,9 @@ export namespace database {
abstract createDatabase(name: string, config: DataBaseConfig): DataBase abstract createDatabase(name: string, config: DataBaseConfig): DataBase
abstract removeDatabase(name: string): boolean abstract removeDatabase(name: string): boolean
abstract getDatabase(name: string): DataBase abstract getDatabase(name: string): DataBase
abstract startWebManager(...args: string[])
abstract stopWebManager()
abstract shutdown()
} }
@injectable() @injectable()
export abstract class DataBase { export abstract class DataBase {

View File

@@ -26,6 +26,7 @@ export namespace event {
private mapEvent = []; private mapEvent = [];
private listenerMap = []; private listenerMap = [];
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 > global.ScriptSlowExecuteTime) { 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,11 +120,11 @@ 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"))
@@ -135,7 +142,7 @@ export namespace event {
// noinspection JSUnusedGlobalSymbols // noinspection JSUnusedGlobalSymbols
var listener = this.register( var listener = this.register(
eventCls, eventCls,
this.execute(name, exec, eventCls), this.createExecute(name, exec, eventCls),
priority, priority,
ignoreCancel ignoreCancel
) )
@@ -171,7 +178,7 @@ export namespace event {
disable(plugin: any) { disable(plugin: any) {
var eventCache = this.listenerMap[plugin.description.name] var eventCache = this.listenerMap[plugin.description.name]
if (eventCache) { if (eventCache) {
eventCache.forEach(off => off()) eventCache.forEach((off: () => any) => off())
delete this.listenerMap[plugin.description.name] delete this.listenerMap[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,11 @@ 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 +28,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 +115,15 @@ export namespace particle {
return this return this
} }
getPlayer() {
return this.player
}
setPlayer(player) {
this.player = player
return this
}
/** /**
* 通过给定一个坐标就可以使用已经指定的参数来播放粒子 * 通过给定一个坐标就可以使用已经指定的参数来播放粒子
* *
@@ -112,9 +131,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 +182,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 +257,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 +345,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 +369,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 +408,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 +503,4 @@ export namespace particle {
} }
} }
} }
export abstract class ParticleSpawner {
abstract spawnParticle(location: any, particle: any, count: number)
abstract spawn(location: any, particle: Particle)
}
} }

View File

@@ -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[] {
@@ -53,6 +54,7 @@ export namespace server {
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,6 @@
{ {
"name": "@ccms/bukkit", "name": "@ccms/bukkit",
"version": "0.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

@@ -2,6 +2,7 @@ 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 URL = Java.type('java.net.URL')
const Bukkit = Java.type("org.bukkit.Bukkit") const Bukkit = Java.type("org.bukkit.Bukkit")
const Event = Java.type("org.bukkit.event.Event") const Event = Java.type("org.bukkit.event.Event")
const Modifier = Java.type("java.lang.reflect.Modifier") const Modifier = Java.type("java.lang.reflect.Modifier")
@@ -19,7 +20,15 @@ export class BukkitEvent extends event.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

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,12 +22,12 @@ 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())
@@ -37,12 +39,14 @@ abstract class BukkitChatInvoke {
if (nmsChatMessageTypeClass.isEnum()) { if (nmsChatMessageTypeClass.isEnum()) {
this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
break break
} else if (nmsChatMessageTypeClass.getName() == 'int') {
break
} }
} }
} }
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
@@ -50,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('.'))
@@ -83,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
@@ -100,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)
} }
@@ -148,18 +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 {
let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
// @ts-ignore // @ts-ignore
let nmsVersion = Bukkit.getServer().class.name.split('.')[3] 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)
} }

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

@@ -3,20 +3,11 @@ 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

@@ -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.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

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

View File

@@ -1,37 +0,0 @@
{
"private": true,
"name": "@ccms/client",
"version": "0.21.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.27.2",
"minecraft-protocol": "^1.34.0",
"minecraft-protocol-forge": "^1.0.0",
"proxy-agent": "^5.0.0"
},
"devDependencies": {
"@types/node": "^17.0.25",
"rimraf": "^3.0.2",
"typescript": "^4.6.3"
}
}

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.21.0", "version": "0.27.6",
"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.21.0", "@ccms/nashorn": "^0.27.6",
"@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.6.3" "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(`切换到超类: ${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(`切换到超类: ${target.getName()}`)
} }
} }
} }

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.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/container", "name": "@ccms/container",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/nashorn": "^0.27.6",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"inversify": "^6.0.1", "inversify": "^6.0.1",

View File

@@ -102,8 +102,8 @@ export const MavenDepend = (groupId: string, artifactId: string, version: string
const loadedMavenDepend = new Set<string>() const loadedMavenDepend = new Set<string>()
export function loadMavenDepend(groupId: string, artifactId: string, version: string, recursion = false) { export function loadMavenDepend(groupId: string, artifactId: string, version: string, recursion = false) {
const key = `${groupId}:${artifactId}:${version}`
try { try {
const key = `${groupId}:${artifactId}:${version}`
if (loadedMavenDepend.has(key)) { return } if (loadedMavenDepend.has(key)) { return }
console.info('loading maven dependency', key) console.info('loading maven dependency', key)
let [pom, _] = base.loadMavenDepend(groupId, artifactId, version) let [pom, _] = base.loadMavenDepend(groupId, artifactId, version)
@@ -137,7 +137,7 @@ export function loadMavenDepend(groupId: string, artifactId: string, version: st
} }
} }
} catch (error: any) { } catch (error: any) {
console.warn('attachMavenDepend failed. Error: ' + error) console.warn('load maven dependency', key, 'failed. Error:', error)
if (global.debug) { if (global.debug) {
console.ex(error) console.ex(error)
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/core", "name": "@ccms/core",
"version": "0.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

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

@@ -0,0 +1 @@
script

View File

@@ -1,13 +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 * 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 * as fs from '@ccms/common/dist/fs'
import { VersionUtils } from '@ccms/common/dist/version'
const UUID = Java.type('java.util.UUID') const UUID = Java.type('java.util.UUID')
const MiaoScriptAPI = Java.type('pw.yumc.MiaoScript.api.MiaoScriptAPI')
@provideSingleton(MiaoScriptCore) @provideSingleton(MiaoScriptCore)
class MiaoScriptCore { class MiaoScriptCore {
@@ -23,8 +25,17 @@ class MiaoScriptCore {
enable() { enable() {
process.emit('core.before.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') 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()
} }
@@ -99,7 +110,7 @@ function loadCoreScript(name) {
try { try {
let scriptname = name + (global.debug ? '-debug' : '') let scriptname = name + (global.debug ? '-debug' : '')
engineLoad({ engineLoad({
script: http.get(`https://ms.yumc.pw/api/plugin/download/name/${scriptname}`), script: http.get(`https://mscript.yumc.pw/api/plugin/download/name/${scriptname}`),
name: `core/${scriptname}.js` name: `core/${scriptname}.js`
}) })
} catch (error: any) { } catch (error: any) {
@@ -123,6 +134,31 @@ function loadMiaoScriptConfig() {
global.ScriptSlowExecuteTime = global.ScriptEngineConfig.slow_execute || 50 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() { function initialize() {
process.emit('core.before.initialize') process.emit('core.before.initialize')
loadMiaoScriptConfig() loadMiaoScriptConfig()
@@ -130,43 +166,16 @@ function initialize() {
global.setGlobal('loadCoreScript', loadCoreScript) global.setGlobal('loadCoreScript', loadCoreScript)
loadCoreScript('initialize') loadCoreScript('initialize')
try { try {
let corePackageStartTime = new Date().getTime() let core = createCore()
container.bind(ContainerInstance).toConstantValue(container) return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable()
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()
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')
let disable = container.get<MiaoScriptCore>(MiaoScriptCore).enable()
console.i18n("ms.core.engine.completed", {
loader: base.version,
version: 'v' + global.ScriptEngineVersion,
time: (Date.now() - global.ScriptEngineStartTime) / 1000
})
process.emit('core.after.initialize')
return disable
} catch (error: any) { } catch (error: any) {
if (console.console) { let core = { enable: () => () => console.i18n('ms.core.engine.disable.abnormal') }
console.i18n("ms.core.initialize.error", { error }) console.i18n("core.initialize.error", { error })
console.ex(error) jsconsole.getStackTrace(error, false).forEach(line => console.log(line))
} else { process.emit('core.initialize.error')
error.printStackTrace() return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable()
} } finally {
return () => console.i18n('ms.core.engine.disable.abnormal') process.emit('core.after.initialize')
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/database", "name": "@ccms/database",
"version": "0.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

@@ -1,6 +1,8 @@
import { database } from '@ccms/api' import { database } from '@ccms/api'
import { JSClass, postConstruct } from '@ccms/container' import { JSClass } from '@ccms/container'
const Thread = Java.type('java.lang.Thread')
const JavaString = Java.type('java.lang.String')
const Properties = Java.type('java.util.Properties') const Properties = Java.type('java.util.Properties')
/** /**
@@ -25,9 +27,21 @@ export class DataBase extends database.DataBase {
private createDataSource(dbConfig: database.DataBaseConfig) { private createDataSource(dbConfig: database.DataBaseConfig) {
if (typeof dbConfig.url === "string") { if (typeof dbConfig.url === "string") {
let originClassLoader = Thread.currentThread().getContextClassLoader()
Thread.currentThread().setContextClassLoader(base.getInstance().class.classLoader)
let config = new this.HikariConfig() let config = new this.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) { if (dbConfig.username) {
config.setUsername(dbConfig.username) config.setUsername(dbConfig.username)
@@ -43,15 +57,17 @@ export class DataBase extends database.DataBase {
} }
config.setDataSourceProperties(properties) config.setDataSourceProperties(properties)
} }
console.debug('createDataSource from config ' + JSON.stringify(dbConfig))
this.dataSource = new this.HikariDataSource(config) 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)
} }
@postConstruct() getDataSource() {
private initialize() { return this.dataSource
this.jdbcTemplate = new this.JdbcTemplate(this.dataSource)
} }
/** /**
@@ -62,7 +78,7 @@ export class DataBase extends database.DataBase {
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
} }
@@ -74,7 +90,7 @@ export class DataBase extends database.DataBase {
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
} }
@@ -85,7 +101,7 @@ export class DataBase extends database.DataBase {
execute(sql: string): void { execute(sql: string): void {
let startTime = Date.now() let startTime = Date.now()
this.jdbcTemplate.execute(sql) this.jdbcTemplate.execute(sql)
console.debug(java.lang.String.format(`\n[DB] execute \nSQL : sql} \nCOST : ${Date.now() - startTime}ms`)) console.debug(`\n[DB] execute \nSQL : ${sql} \nCOST : ${Date.now() - startTime}ms`)
} }
close() { close() {

View File

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

View File

@@ -1,5 +1,5 @@
import { database } from '@ccms/api' import { database } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { JSClass, provideSingleton } from '@ccms/container'
import { DataBase } from './database' import { DataBase } from './database'
@provideSingleton(database.DataBaseManager) @provideSingleton(database.DataBaseManager)
@@ -7,11 +7,32 @@ export class DataBaseManager extends database.DataBaseManager {
private mainDatabase: DataBase private mainDatabase: DataBase
private databases = new Map<string, DataBase>() private databases = new Map<string, DataBase>()
@JSClass('org.h2.tools.Server')
private Server: any
private webManager: any
constructor() { constructor() {
super() super()
process.on('exit', () => this.disable()) 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 主数据库 * @param mainDatabase 主数据库

View File

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

View File

@@ -34,7 +34,7 @@ ms.api.command.tab.completer.slow: "§c注意! §6玩家 §a{player} §6执行
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.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} 个插件 开始编译..."

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/i18n", "name": "@ccms/i18n",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/nashorn": "^0.27.6",
"@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.6.3" "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.21.0", "version": "0.27.6",
"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.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.21.0", "@ccms/nashorn": "^0.27.6",
"@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.6.3" "typescript": "^4.9.5"
}, },
"gitHead": "2589633069d24f646ac09261b1b2304c21d4ea75" "gitHead": "2589633069d24f646ac09261b1b2304c21d4ea75"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/molang", "name": "@ccms/molang",
"version": "0.21.0", "version": "0.27.6",
"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.25", "@types/node": "^18.13.0",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"tslib": "^2.3.1", "tslib": "^2.5.0",
"typescript": "^4.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/nashorn", "name": "@ccms/nashorn",
"version": "0.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -48,7 +48,10 @@ declare global {
scope: string scope: string
logger: any logger: any
debug: boolean debug: boolean
level: string /**
* 引擎日志等级
*/
ScriptEngineLoggerLevel: string
/** /**
* 引擎配置 * 引擎配置
*/ */
@@ -61,8 +64,18 @@ declare global {
* 引擎渠道 * 引擎渠道
*/ */
ScriptEngineChannel: string ScriptEngineChannel: string
/**
* 慢执行检测时间
*/
ScriptSlowExecuteTime: number 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

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nodejs", "name": "@ccms/nodejs",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/nashorn": "^0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"gitHead": "781524f83e52cad26d7c480513e3c525df867121" "gitHead": "781524f83e52cad26d7c480513e3c525df867121"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/nukkit", "name": "@ccms/nukkit",
"version": "0.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/plugin", "name": "@ccms/plugin",
"version": "0.21.0", "version": "0.27.6",
"description": "MiaoScript api package", "description": "MiaoScript plugin package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -22,15 +22,15 @@
"@types/crypto-js": "^4.1.1", "@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.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0", "@ccms/container": "^0.27.6",
"@ccms/i18n": "^0.21.0", "@ccms/i18n": "^0.27.6",
"@ccms/verify": "^0.21.1", "@ccms/verify": "^0.25.1",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0"
} }

View File

@@ -78,7 +78,7 @@ export class PluginCommandManager {
permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand || 'main'}` permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand || 'main'}`
} }
if (!sender.hasPermission(permission)) { if (!sender.hasPermission(permission)) {
return pluginInstance.logger.sender(sender, `§c你需要 ${permission} 权限 才可执行此命令.`) return pluginInstance.logger.sender(sender, `§c你需要 §4${permission} §c权限 才可执行此命令.`)
} }
} }
args.shift() args.shift()
@@ -98,7 +98,7 @@ export class PluginCommandManager {
return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(pluginInstance, [sender, command, args]) || []) return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(pluginInstance, [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

@@ -7,8 +7,7 @@ import { interfaces } from './interfaces'
import { getPluginConfigMetadata } from './utils' import { getPluginConfigMetadata } from './utils'
import { PluginConfigLoader } from './config/interfaces' import { PluginConfigLoader } from './config/interfaces'
import './config/loader/json-loader' import './config/loader'
import './config/loader/yaml-loader'
@provideSingleton(PluginConfigManager) @provideSingleton(PluginConfigManager)
export class PluginConfigManager { export class PluginConfigManager {
@@ -68,30 +67,25 @@ export class PluginConfigManager {
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( plugin.description.name,
fs.file(plugin.description.loadMetadata.file).parent, metadata.filename
plugin.description.name, )
metadata.filename let configLoader = this.getConfigLoader(metadata.format)
) if (!fs.exists(metadata.file) && defaultValue) {
let configLoader = this.getConfigLoader(metadata.format) base.save(metadata.file, configLoader.dump(defaultValue))
if (!fs.exists(metadata.file)) { console.i18n("ms.plugin.manager.config.save.default", {
base.save(metadata.file, configLoader.dump(defaultValue)) plugin: plugin.description.name,
console.i18n("ms.plugin.manager.config.save.default", { name: metadata.name,
plugin: plugin.description.name, format: metadata.format
name: metadata.name, })
format: metadata.format
})
} else {
configValue = configLoader.load(base.read(metadata.file)) || {}
if (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} =>
${JSON.stringify(configValue, undefined, 4).substring(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", { console.i18n("ms.plugin.manager.config.load.error", {

View File

@@ -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)

View File

@@ -5,9 +5,11 @@ 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
constructor() { constructor() {
process.on('plugin.before.enable', this.registryListener.bind(this)) process.on('plugin.before.enable', this.registryListener.bind(this))
@@ -15,26 +17,31 @@ export class PluginEventManager {
} }
mapEventName() { mapEventName() {
return this.EventManager.mapEventName().toFixed(0) return this.eventManager.mapEventName().toFixed(0)
} }
private registryListener(pluginInstance: plugin.Plugin) { private registryListener(pluginInstance: plugin.Plugin) {
let events = getPluginListenerMetadata(pluginInstance) let events = getPluginListenerMetadata(pluginInstance)
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(pluginInstance)
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)
} }
} }
private unregistryListener(pluginInstance: plugin.Plugin) { private unregistryListener(pluginInstance: plugin.Plugin) {
this.EventManager.disable(pluginInstance) this.eventManager.disable(pluginInstance)
} }
} }

View File

@@ -73,6 +73,10 @@ export namespace interfaces {
* 是否忽略已取消的事件 * 是否忽略已取消的事件
*/ */
ignoreCancel?: boolean ignoreCancel?: boolean
/**
* 依赖插件 没有就不加载
*/
plugins?: string[]
} }
export interface ConfigMetadata extends plugin.BaseMetadata { export interface ConfigMetadata extends plugin.BaseMetadata {
/** /**
@@ -91,6 +95,10 @@ export namespace interfaces {
* 配置文件格式 默认 yml * 配置文件格式 默认 yml
*/ */
format?: string format?: string
/**
* 是否合并默认配置
*/
migrate?: boolean
/** /**
* 自动保存 默认为 false * 自动保存 默认为 false
*/ */

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')
@@ -69,7 +68,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
try { try {
console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType })
} catch (error) { } catch (error) {
console.i18n('ms.plugin.event.map.error', { 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) => {
@@ -93,18 +92,17 @@ export class PluginManagerImpl implements plugin.PluginManager {
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)
} }
} }
@@ -229,8 +227,16 @@ export class PluginManagerImpl implements plugin.PluginManager {
}) })
} }
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() {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/polyfill", "name": "@ccms/polyfill",
"version": "0.21.0", "version": "0.27.6",
"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.21.0", "@ccms/i18n": "^0.27.6",
"@ccms/nodejs": "^0.21.0", "@ccms/nodejs": "^0.27.6",
"core-js": "^3.22.1" "core-js": "^3.27.2"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.21.0", "@ccms/nashorn": "^0.27.6",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "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,9 +82,44 @@ 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]"
} }
@@ -76,16 +128,16 @@ class Process extends EventEmitter {
class EventLoop { class EventLoop {
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, 1, 1, 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")
thread.setDaemon(true) thread.setDaemon(true)
return thread return thread
})) }))
@@ -115,7 +167,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,7 +189,9 @@ 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: () => {
@@ -153,13 +207,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}s!`)
} }
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.21.0", "version": "0.27.6",
"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.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -0,0 +1,26 @@
{
"name": "@ccms/qrcode",
"version": "0.27.6",
"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

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ccms/sponge", "name": "@ccms/sponge",
"version": "0.21.0", "version": "0.27.6",
"description": "MiaoScript api package", "description": "MiaoScript sponge package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
"minecraft", "minecraft",
@@ -21,12 +21,12 @@
"devDependencies": { "devDependencies": {
"@javatypes/sponge-api": "^0.0.3", "@javatypes/sponge-api": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

@@ -1,32 +1,15 @@
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 SpongeParticleManager extends particle.ParticleManager { export class SpongeParticleManager extends particle.ParticleManager {
private globalSpawner = new SpongeParticleSpawner()
constructor() {
super()
particle.ParticleManager.globalSpawner = this.globalSpawner
}
protected getGlobalSpawner() {
return this.globalSpawner
}
} }
@provideSingleton(particle.ParticleSpawner)
export class SpongeParticleSpawner extends particle.ParticleSpawner { export class SpongeParticleSpawner extends particle.ParticleSpawner {
spawnParticle(location: org.spongepowered.api.world.Location<any>, particle: any, count: number = 1) {
location.getPosition()
// location.getWorld().spawnParticle(particle, location, count)
}
spawn(location: any, particle: particle.Particle) { spawn(location: any, particle: particle.Particle) {
location.getWorld().spawnParticle( throw new Error('Not Impl.')
particle.getParticle(), }
location, spawnToPlayer(player: any, location: any, particle: particle.Particle) {
particle.getCount(), throw new Error('Not Impl.')
particle.getOffsetX(),
particle.getOffsetY(),
particle.getOffsetZ(),
particle.getExtra(),
particle.getData()
)
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/spring", "name": "@ccms/spring",
"version": "0.21.0", "version": "0.27.6",
"description": "MiaoScript spring package", "description": "MiaoScript spring package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -20,13 +20,13 @@
}, },
"devDependencies": { "devDependencies": {
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/common": "^0.21.0", "@ccms/common": "^0.27.6",
"@ccms/container": "^0.21.0", "@ccms/container": "^0.27.6",
"@ccms/database": "^0.21.0" "@ccms/database": "^0.27.6"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/web", "name": "@ccms/web",
"version": "0.21.0", "version": "0.27.6",
"description": "MiaoScript web package", "description": "MiaoScript web package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -25,11 +25,11 @@
"@javatypes/spring-web": "^0.0.3", "@javatypes/spring-web": "^0.0.3",
"@javatypes/tomcat": "^0.0.3", "@javatypes/tomcat": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@ccms/api": "^0.21.0", "@ccms/api": "^0.27.6",
"@ccms/container": "^0.21.0" "@ccms/container": "^0.27.6"
} }
} }

View File

@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"semi": true
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ccms/websocket", "name": "@ccms/websocket",
"version": "0.21.0", "version": "0.27.6",
"description": "MiaoScript websocket package", "description": "MiaoScript websocket package",
"keywords": [ "keywords": [
"miaoscript", "miaoscript",
@@ -19,14 +19,15 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@socket.io/component-emitter": "3.1.0",
"backo2": "^1.0.2", "backo2": "^1.0.2",
"parseuri": "^0.0.6" "parseuri": "^0.0.6"
}, },
"devDependencies": { "devDependencies": {
"@ccms/nashorn": "^0.21.0", "@ccms/nashorn": "^0.27.6",
"@javatypes/tomcat-websocket-api": "^0.0.3", "@javatypes/tomcat-websocket-api": "^0.0.3",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^4.1.2",
"typescript": "^4.6.3" "typescript": "^4.9.5"
} }
} }

View File

@@ -38,7 +38,7 @@ export class WebSocket extends EventEmitter {
private client: Transport private client: Transport
constructor(url: string, subProtocol: string = '', headers: WebSocketHeader = {}) { constructor(url: string, subProtocol: string | string[] = '', headers: WebSocketHeader = {}) {
super() super()
this.manager = manager this.manager = manager
this._url = url this._url = url

View File

@@ -58,7 +58,7 @@ export class NettyWebSocket extends Transport {
private _host: string private _host: string
private _port: number private _port: number
private channel: any private channel: any
private b = new Bootstrap(); private b: any
constructor(url: string, subProtocol: string = '', headers: WebSocketHeader = {}) { constructor(url: string, subProtocol: string = '', headers: WebSocketHeader = {}) {
super(url, subProtocol, headers) super(url, subProtocol, headers)
@@ -102,6 +102,7 @@ export class NettyWebSocket extends Transport {
// HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline. // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
let handler = new WebSocketClientHandler(WebSocketClientHandshakerFactory let handler = new WebSocketClientHandler(WebSocketClientHandshakerFactory
.newHandshaker(uri, WebSocketVersion.V13, null, false, headers), this) .newHandshaker(uri, WebSocketVersion.V13, null, false, headers), this)
this.b = new Bootstrap()
this.b.group(group) this.b.group(group)
.channel(socketChannelClass) .channel(socketChannelClass)
.handler(new ChannelInitializer({ .handler(new ChannelInitializer({
@@ -123,18 +124,22 @@ export class NettyWebSocket extends Transport {
} }
})) }))
this.b.connect(this._host, this._port).addListener(new ChannelFutureListener((future: any) => { this.b.connect(this._host, this._port).addListener(new ChannelFutureListener((future: any) => {
this.channel = future.sync().channel() try {
this.onconnection({}) this.channel = future.sync().channel()
handler.handshakeFuture.addListener(new ChannelFutureListener((future: any) => { this.onconnection({})
try { handler.handshakeFuture.addListener(new ChannelFutureListener((future: any) => {
future.sync() try {
// only trigger onconnect when not have error future.sync()
this.onconnect({}) // only trigger onconnect when not have error
} catch (error: any) { this.onconnect({})
// ignore error exceptionCaught from handler } catch (error: any) {
// this.onerror({ error }) // ignore error exceptionCaught from handler
} // this.onerror({ error })
})) }
}))
} catch (error: any) {
this.onerror({ error })
}
})) }))
} }
doSend(text: string) { doSend(text: string) {

View File

@@ -1 +1,57 @@
export = (namepsace) => (...args) => { }//console.debug(namepsace, ...args) export = (namepsace) =>
(...args) => {
console.trace(`[${namepsace}] ` + format(...args))
} //console.debug(namepsace, ...args)
let formatters: any = {}
formatters.s = function (v) {
return v
}
formatters.j = function (v) {
try {
return JSON.stringify(v)
} catch (error: any) {
return "[UnexpectedJSONParseError]: " + error.message
}
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) {
return val.stack || val.message
}
return val
}
function format(...args) {
// Apply any `formatters` transformations
args[0] = coerce(args[0])
if (typeof args[0] !== "string") {
// Anything else let's inspect with %O
args.unshift("%O")
}
let index = 0
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === "%%") {
return "%"
}
index++
const formatter = formatters[format]
if (typeof formatter === "function") {
const val = args[index]
match = formatter.call(format, val)
// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1)
index--
}
return match
})
return args[0]
}

View File

@@ -0,0 +1,12 @@
// imported from https://github.com/component/has-cors
let value = false;
try {
value = typeof XMLHttpRequest !== 'undefined' &&
'withCredentials' in new XMLHttpRequest();
} catch (err) {
// if XMLHttp support is disabled in IE then it will throw
// when trying to create
}
export const hasCORS = value;

View File

@@ -0,0 +1,38 @@
// imported from https://github.com/galkn/querystring
/**
* Compiles a querystring
* Returns string representation of the object
*
* @param {Object}
* @api private
*/
export function encode(obj) {
let str = ''
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
if (str.length) str += '&'
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i])
}
}
return str
}
/**
* Parses a simple querystring into an object
*
* @param {String} qs
* @api private
*/
export function decode(qs) {
let qry = {}
let pairs = qs.split('&')
for (let i = 0, l = pairs.length; i < l; i++) {
let pair = pairs[i].split('=')
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1])
}
return qry
}

View File

@@ -0,0 +1,68 @@
// imported from https://github.com/galkn/parseuri
/**
* Parses an URI
*
* @author Steven Levithan <stevenlevithan.com> (MIT license)
* @api private
*/
const re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
const parts = [
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
]
export function parse(str) {
const src = str,
b = str.indexOf('['),
e = str.indexOf(']')
if (b != -1 && e != -1) {
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length)
}
let m = re.exec(str || ''),
uri = {} as any,
i = 14
while (i--) {
uri[parts[i]] = m[i] || ''
}
if (b != -1 && e != -1) {
uri.source = src
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':')
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':')
uri.ipv6uri = true
}
uri.pathNames = pathNames(uri, uri['path'])
uri.queryKey = queryKey(uri, uri['query'])
return uri
}
function pathNames(obj, path) {
const regx = /\/{2,9}/g,
names = path.replace(regx, "/").split("/")
if (path.slice(0, 1) == '/' || path.length === 0) {
names.splice(0, 1)
}
if (path.slice(-1) == '/') {
names.splice(names.length - 1, 1)
}
return names
}
function queryKey(uri, query) {
const data = {}
query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) {
if ($1) {
data[$1] = $2
}
})
return data
}

View File

@@ -0,0 +1,62 @@
// imported from https://github.com/unshiftio/yeast
'use strict'
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('')
, length = 64
, map = {}
let seed = 0
, i = 0
, prev
/**
* Return a string representing the specified number.
*
* @param {Number} num The number to convert.
* @returns {String} The string representation of the number.
* @api public
*/
export function encode(num) {
let encoded = ''
do {
encoded = alphabet[num % length] + encoded
num = Math.floor(num / length)
} while (num > 0)
return encoded
}
/**
* Return the integer value specified by the given string.
*
* @param {String} str The string to convert.
* @returns {Number} The integer value represented by the string.
* @api public
*/
export function decode(str) {
let decoded = 0
for (i = 0; i < str.length; i++) {
decoded = decoded * length + map[str.charAt(i)]
}
return decoded
}
/**
* Yeast: A tiny growing id generator.
*
* @returns {String} A unique id.
* @api public
*/
export function yeast() {
const now = encode(+new Date())
if (now !== prev) return seed = 0, prev = now
return now + '.' + encode(seed++)
}
//
// Map each character to its index.
//
for (; i < length; i++) map[alphabet[i]] = i

View File

@@ -1,16 +1,10 @@
import { Socket } from './socket' import { Socket } from "./socket"
export default (uri, opts) => new Socket(uri, opts) export { Socket }
export { SocketOptions } from "./socket"
/** export const protocol = Socket.protocol
* Expose deps for legacy compatibility export { Transport } from "./transport"
* and standalone browser access. export { transports } from "./transports/index"
*/ export { installTimerFunctions } from "./util"
const protocol = Socket.protocol // this is an int export { parse } from "./contrib/parseuri"
export { Socket, protocol } export { nextTick } from "./transports/websocket-constructor"
// module.exports.Transport = require("./transport")
// module.exports.transports = require("./transports/index")
// module.exports.parser = require("../engine.io-parser")
export * from './transport'
export * from './transports/index'
export * from '../engine.io-parser'

View File

@@ -1,21 +1,295 @@
import transports from "./transports" // import { transports } from "./transports/index.js";
// const transports = require("./transports/index") import { transports } from "./transports"
const Emitter = require("component-emitter") import { installTimerFunctions, byteLength } from "./util"
const debug = (...args: any) => console.debug('engine.io-client:socket', ...args)//require("debug")("engine.io-client:socket") import { decode } from "./contrib/parseqs"
import parser from "../engine.io-parser" import { parse } from "./contrib/parseuri"
const parseuri = require("parseuri") // import debugModule from "debug"; // debug()
const parseqs = require("parseqs") import { Emitter } from "@socket.io/component-emitter"
import { installTimerFunctions } from "./util" // import { protocol } from "engine.io-parser";
import { protocol } from "../engine.io-parser"
import type { Packet, BinaryType, PacketType, RawData } from "../engine.io-parser"
import { CloseDetails, Transport } from "./transport"
// const debug = debugModule("engine.io-client:socket"); // debug()
const debug = require('../debug')('engine.io-client:socket')
export interface SocketOptions {
/**
* The host that we're connecting to. Set from the URI passed when connecting
*/
host: string
/**
* The hostname for our connection. Set from the URI passed when connecting
*/
hostname: string
/**
* If this is a secure connection. Set from the URI passed when connecting
*/
secure: boolean
/**
* The port for our connection. Set from the URI passed when connecting
*/
port: string | number
/**
* Any query parameters in our uri. Set from the URI passed when connecting
*/
query: { [key: string]: any }
/**
* `http.Agent` to use, defaults to `false` (NodeJS only)
*/
agent: string | boolean
/**
* Whether the client should try to upgrade the transport from
* long-polling to something better.
* @default true
*/
upgrade: boolean
/**
* Forces base 64 encoding for polling transport even when XHR2
* responseType is available and WebSocket even if the used standard
* supports binary.
*/
forceBase64: boolean
/**
* The param name to use as our timestamp key
* @default 't'
*/
timestampParam: string
/**
* Whether to add the timestamp with each transport request. Note: this
* is ignored if the browser is IE or Android, in which case requests
* are always stamped
* @default false
*/
timestampRequests: boolean
/**
* A list of transports to try (in order). Engine.io always attempts to
* connect directly with the first one, provided the feature detection test
* for it passes.
* @default ['polling','websocket']
*/
transports: string[]
/**
* The port the policy server listens on
* @default 843
*/
policyPost: number
/**
* If true and if the previous websocket connection to the server succeeded,
* the connection attempt will bypass the normal upgrade process and will
* initially try websocket. A connection attempt following a transport error
* will use the normal upgrade process. It is recommended you turn this on
* only when using SSL/TLS connections, or if you know that your network does
* not block websockets.
* @default false
*/
rememberUpgrade: boolean
/**
* Are we only interested in transports that support binary?
*/
onlyBinaryUpgrades: boolean
/**
* Timeout for xhr-polling requests in milliseconds (0) (only for polling transport)
*/
requestTimeout: number
/**
* Transport options for Node.js client (headers etc)
*/
transportOptions: Object
/**
* (SSL) Certificate, Private key and CA certificates to use for SSL.
* Can be used in Node.js client environment to manually specify
* certificate information.
*/
pfx: string
/**
* (SSL) Private key to use for SSL. Can be used in Node.js client
* environment to manually specify certificate information.
*/
key: string
/**
* (SSL) A string or passphrase for the private key or pfx. Can be
* used in Node.js client environment to manually specify certificate
* information.
*/
passphrase: string
/**
* (SSL) Public x509 certificate to use. Can be used in Node.js client
* environment to manually specify certificate information.
*/
cert: string
/**
* (SSL) An authority certificate or array of authority certificates to
* check the remote host against.. Can be used in Node.js client
* environment to manually specify certificate information.
*/
ca: string | string[]
/**
* (SSL) A string describing the ciphers to use or exclude. Consult the
* [cipher format list]
* (http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT) for
* details on the format.. Can be used in Node.js client environment to
* manually specify certificate information.
*/
ciphers: string
/**
* (SSL) If true, the server certificate is verified against the list of
* supplied CAs. An 'error' event is emitted if verification fails.
* Verification happens at the connection level, before the HTTP request
* is sent. Can be used in Node.js client environment to manually specify
* certificate information.
*/
rejectUnauthorized: boolean
/**
* Headers that will be passed for each request to the server (via xhr-polling and via websockets).
* These values then can be used during handshake or for special proxies.
*/
extraHeaders?: { [header: string]: string }
/**
* Whether to include credentials (cookies, authorization headers, TLS
* client certificates, etc.) with cross-origin XHR polling requests
* @default false
*/
withCredentials: boolean
/**
* Whether to automatically close the connection whenever the beforeunload event is received.
* @default true
*/
closeOnBeforeunload: boolean
/**
* Whether to always use the native timeouts. This allows the client to
* reconnect when the native timeout functions are overridden, such as when
* mock clocks are installed.
* @default false
*/
useNativeTimers: boolean
/**
* weather we should unref the reconnect timer when it is
* create automatically
* @default false
*/
autoUnref: boolean
/**
* parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
* @default false
*/
perMessageDeflate: { threshold: number }
/**
* The path to get our client file from, in the case of the server
* serving it
* @default '/engine.io'
*/
path: string
/**
* Whether we should add a trailing slash to the request path.
* @default true
*/
addTrailingSlash: boolean
/**
* Either a single protocol string or an array of protocol strings. These strings are used to indicate sub-protocols,
* so that a single server can implement multiple WebSocket sub-protocols (for example, you might want one server to
* be able to handle different types of interactions depending on the specified protocol)
* @default []
*/
protocols: string | string[]
}
interface HandshakeData {
sid: string
upgrades: string[]
pingInterval: number
pingTimeout: number
maxPayload: number
}
interface SocketReservedEvents {
open: () => void
handshake: (data: HandshakeData) => void
packet: (packet: Packet) => void
packetCreate: (packet: Packet) => void
data: (data) => void
message: (data) => void
drain: () => void
flush: () => void
heartbeat: () => void
ping: () => void
pong: () => void
error: (err: string | Error) => void
upgrading: (transport) => void
upgrade: (transport) => void
upgradeError: (err: Error) => void
close: (reason: string, description?: CloseDetails | Error) => void
}
type SocketState = "opening" | "open" | "closing" | "closed"
export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
public id: string
public transport: Transport
public binaryType: BinaryType
public readyState: SocketState
public writeBuffer: Packet[] = [];
private prevBufferLen: number
private upgrades
private pingInterval: number
private pingTimeout: number
private pingTimeoutTimer: NodeJS.Timer
private setTimeoutFn: typeof setTimeout
private clearTimeoutFn: typeof clearTimeout
private readonly beforeunloadEventListener: () => void
private readonly offlineEventListener: () => void
private upgrading: boolean
private maxPayload?: number
private readonly opts: Partial<SocketOptions>
private readonly secure: boolean
private readonly hostname: string
private readonly port: string | number
private readonly transports: string[]
static priorWebsocketSuccess: boolean
static protocol = protocol;
export class Socket extends Emitter {
/** /**
* Socket constructor. * Socket constructor.
* *
* @param {String|Object} uri or options * @param {String|Object} uri or options
* @param {Object} options * @param {Object} opts - options
* @api public * @api public
*/ */
constructor(uri, opts: any = {}) { constructor(uri, opts: Partial<SocketOptions> = {}) {
super() super()
if (uri && "object" === typeof uri) { if (uri && "object" === typeof uri) {
@@ -24,13 +298,13 @@ export class Socket extends Emitter {
} }
if (uri) { if (uri) {
uri = parseuri(uri) uri = parse(uri)
opts.hostname = uri.host opts.hostname = uri.host
opts.secure = uri.protocol === "https" || uri.protocol === "wss" opts.secure = uri.protocol === "https" || uri.protocol === "wss"
opts.port = uri.port opts.port = uri.port
if (uri.query) opts.query = uri.query if (uri.query) opts.query = uri.query
} else if (opts.host) { } else if (opts.host) {
opts.hostname = parseuri(opts.host).host opts.hostname = parse(opts.host).host
} }
installTimerFunctions(this, opts) installTimerFunctions(this, opts)
@@ -53,11 +327,10 @@ export class Socket extends Emitter {
(typeof location !== "undefined" && location.port (typeof location !== "undefined" && location.port
? location.port ? location.port
: this.secure : this.secure
? 443 ? "443"
: 80) : "80")
this.transports = ["websocket"] this.transports = opts.transports || ["polling", "websocket"]
this.readyState = ""
this.writeBuffer = [] this.writeBuffer = []
this.prevBufferLen = 0 this.prevBufferLen = 0
@@ -67,9 +340,9 @@ export class Socket extends Emitter {
agent: false, agent: false,
withCredentials: false, withCredentials: false,
upgrade: true, upgrade: true,
jsonp: true,
timestampParam: "t", timestampParam: "t",
rememberUpgrade: false, rememberUpgrade: false,
addTrailingSlash: true,
rejectUnauthorized: true, rejectUnauthorized: true,
perMessageDeflate: { perMessageDeflate: {
threshold: 1024 threshold: 1024
@@ -80,10 +353,12 @@ export class Socket extends Emitter {
opts opts
) )
this.opts.path = this.opts.path.replace(/\/$/, "") + "/" this.opts.path =
this.opts.path.replace(/\/$/, "") +
(this.opts.addTrailingSlash ? "/" : "")
if (typeof this.opts.query === "string") { if (typeof this.opts.query === "string") {
this.opts.query = parseqs.decode(this.opts.query) this.opts.query = decode(this.opts.query)
} }
// set on handshake // set on handshake
@@ -100,21 +375,20 @@ export class Socket extends Emitter {
// Firefox closes the connection when the "beforeunload" event is emitted but not Chrome. This event listener // Firefox closes the connection when the "beforeunload" event is emitted but not Chrome. This event listener
// ensures every browser behaves the same (no "disconnect" event at the Socket.IO level when the page is // ensures every browser behaves the same (no "disconnect" event at the Socket.IO level when the page is
// closed/reloaded) // closed/reloaded)
addEventListener( this.beforeunloadEventListener = () => {
"beforeunload", if (this.transport) {
() => { // silently close the transport
if (this.transport) { this.transport.removeAllListeners()
// silently close the transport this.transport.close()
this.transport.removeAllListeners() }
this.transport.close() }
} addEventListener("beforeunload", this.beforeunloadEventListener, false)
},
false
)
} }
if (this.hostname !== "localhost") { if (this.hostname !== "localhost") {
this.offlineEventListener = () => { this.offlineEventListener = () => {
this.onClose("transport close") this.onClose("transport close", {
description: "network connection lost",
})
} }
addEventListener("offline", this.offlineEventListener, false) addEventListener("offline", this.offlineEventListener, false)
} }
@@ -126,19 +400,16 @@ export class Socket extends Emitter {
/** /**
* Creates transport of the given type. * Creates transport of the given type.
* *
* @param {String} transport name * @param {String} name - transport name
* @return {Transport} * @return {Transport}
* @api private * @private
*/ */
createTransport(name, opt?) { private createTransport(name) {
if (name != 'websocket') {
throw new Error('Only Support WebSocket in MiaoScript!')
}
debug('creating transport "%s"', name) debug('creating transport "%s"', name)
const query: any = clone(this.opts.query) const query: any = Object.assign({}, this.opts.query)
// append engine.io protocol identifier // append engine.io protocol identifier
query.EIO = parser.protocol query.EIO = protocol
// transport name // transport name
query.transport = name query.transport = name
@@ -155,21 +426,21 @@ export class Socket extends Emitter {
socket: this, socket: this,
hostname: this.hostname, hostname: this.hostname,
secure: this.secure, secure: this.secure,
port: this.port port: this.port,
} }
) )
debug("options: %j", JSON.stringify(opts)) debug("options: %j", opts)
debug("new func", transports[name])
return new transports[name](opts) return new transports[name](opts)
} }
/** /**
* Initializes transport to use and starts probe. * Initializes transport to use and starts probe.
* *
* @api private * @private
*/ */
open() { private open() {
let transport let transport
if ( if (
this.opts.rememberUpgrade && this.opts.rememberUpgrade &&
@@ -180,7 +451,7 @@ export class Socket extends Emitter {
} else if (0 === this.transports.length) { } else if (0 === this.transports.length) {
// Emit error on next tick so it can be listened to // Emit error on next tick so it can be listened to
this.setTimeoutFn(() => { this.setTimeoutFn(() => {
this.emit("error", "No transports available") this.emitReserved("error", "No transports available")
}, 0) }, 0)
return return
} else { } else {
@@ -191,8 +462,8 @@ export class Socket extends Emitter {
// Retry with the next transport if the transport is disabled (jsonp: false) // Retry with the next transport if the transport is disabled (jsonp: false)
try { try {
transport = this.createTransport(transport) transport = this.createTransport(transport)
} catch (error: any) { } catch (e) {
debug("error while creating transport: %s", error) debug("error while creating transport: %s", e)
this.transports.shift() this.transports.shift()
this.open() this.open()
return return
@@ -205,9 +476,9 @@ export class Socket extends Emitter {
/** /**
* Sets the current transport. Disables the existing one (if any). * Sets the current transport. Disables the existing one (if any).
* *
* @api private * @private
*/ */
setTransport(transport) { private setTransport(transport) {
debug("setting transport %s", transport.name) debug("setting transport %s", transport.name)
if (this.transport) { if (this.transport) {
@@ -223,9 +494,7 @@ export class Socket extends Emitter {
.on("drain", this.onDrain.bind(this)) .on("drain", this.onDrain.bind(this))
.on("packet", this.onPacket.bind(this)) .on("packet", this.onPacket.bind(this))
.on("error", this.onError.bind(this)) .on("error", this.onError.bind(this))
.on("close", () => { .on("close", (reason) => this.onClose("transport close", reason))
this.onClose("transport close")
})
} }
/** /**
@@ -234,9 +503,9 @@ export class Socket extends Emitter {
* @param {String} transport name * @param {String} transport name
* @api private * @api private
*/ */
probe(name) { private probe(name) {
debug('probing transport "%s"', name) debug('probing transport "%s"', name)
let transport = this.createTransport(name, { probe: 1 }) let transport = this.createTransport(name)
let failed = false let failed = false
Socket.priorWebsocketSuccess = false Socket.priorWebsocketSuccess = false
@@ -251,7 +520,7 @@ export class Socket extends Emitter {
if ("pong" === msg.type && "probe" === msg.data) { if ("pong" === msg.type && "probe" === msg.data) {
debug('probe transport "%s" pong', name) debug('probe transport "%s" pong', name)
this.upgrading = true this.upgrading = true
this.emit("upgrading", transport) this.emitReserved("upgrading", transport)
if (!transport) return if (!transport) return
Socket.priorWebsocketSuccess = "websocket" === transport.name Socket.priorWebsocketSuccess = "websocket" === transport.name
@@ -265,16 +534,17 @@ export class Socket extends Emitter {
this.setTransport(transport) this.setTransport(transport)
transport.send([{ type: "upgrade" }]) transport.send([{ type: "upgrade" }])
this.emit("upgrade", transport) this.emitReserved("upgrade", transport)
transport = null transport = null
this.upgrading = false this.upgrading = false
this.flush() this.flush()
}) })
} else { } else {
debug('probe transport "%s" failed', name) debug('probe transport "%s" failed', name)
const err: any = new Error("probe error") const err = new Error("probe error")
// @ts-ignore
err.transport = transport.name err.transport = transport.name
this.emit("upgradeError", err) this.emitReserved("upgradeError", err)
} }
}) })
} }
@@ -293,14 +563,15 @@ export class Socket extends Emitter {
// Handle any error that happens while probing // Handle any error that happens while probing
const onerror = err => { const onerror = err => {
const error: any = new Error("probe error: " + err) const error = new Error("probe error: " + err)
// @ts-ignore
error.transport = transport.name error.transport = transport.name
freezeTransport() freezeTransport()
debug('probe transport "%s" failed because of error: %s', name, err) debug('probe transport "%s" failed because of error: %s', name, err)
this.emit("upgradeError", error) this.emitReserved("upgradeError", error)
} }
function onTransportClose() { function onTransportClose() {
@@ -325,8 +596,8 @@ export class Socket extends Emitter {
transport.removeListener("open", onTransportOpen) transport.removeListener("open", onTransportOpen)
transport.removeListener("error", onerror) transport.removeListener("error", onerror)
transport.removeListener("close", onTransportClose) transport.removeListener("close", onTransportClose)
this.removeListener("close", onclose) this.off("close", onclose)
this.removeListener("upgrading", onupgrade) this.off("upgrading", onupgrade)
} }
transport.once("open", onTransportOpen) transport.once("open", onTransportOpen)
@@ -342,13 +613,13 @@ export class Socket extends Emitter {
/** /**
* Called when connection is deemed open. * Called when connection is deemed open.
* *
* @api public * @api private
*/ */
onOpen() { private onOpen() {
debug("socket open") debug("socket open")
this.readyState = "open" this.readyState = "open"
Socket.priorWebsocketSuccess = "websocket" === this.transport.name Socket.priorWebsocketSuccess = "websocket" === this.transport.name
this.emit("open") this.emitReserved("open")
this.flush() this.flush()
// we check for `readyState` in case an `open` // we check for `readyState` in case an `open`
@@ -372,7 +643,7 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
onPacket(packet) { private onPacket(packet) {
if ( if (
"opening" === this.readyState || "opening" === this.readyState ||
"open" === this.readyState || "open" === this.readyState ||
@@ -380,10 +651,10 @@ export class Socket extends Emitter {
) { ) {
debug('socket receive: type "%s", data "%s"', packet.type, packet.data) debug('socket receive: type "%s", data "%s"', packet.type, packet.data)
this.emit("packet", packet) this.emitReserved("packet", packet)
// Socket is live - any packet counts // Socket is live - any packet counts
this.emit("heartbeat") this.emitReserved("heartbeat")
switch (packet.type) { switch (packet.type) {
case "open": case "open":
@@ -393,19 +664,20 @@ export class Socket extends Emitter {
case "ping": case "ping":
this.resetPingTimeout() this.resetPingTimeout()
this.sendPacket("pong") this.sendPacket("pong")
this.emit("ping") this.emitReserved("ping")
this.emit("pong") this.emitReserved("pong")
break break
case "error": case "error":
const err: any = new Error("server error") const err = new Error("server error")
// @ts-ignore
err.code = packet.data err.code = packet.data
this.onError(err) this.onError(err)
break break
case "message": case "message":
this.emit("data", packet.data) this.emitReserved("data", packet.data)
this.emit("message", packet.data) this.emitReserved("message", packet.data)
break break
} }
} else { } else {
@@ -416,16 +688,17 @@ export class Socket extends Emitter {
/** /**
* Called upon handshake completion. * Called upon handshake completion.
* *
* @param {Object} handshake obj * @param {Object} data - handshake obj
* @api private * @api private
*/ */
onHandshake(data) { private onHandshake(data) {
this.emit("handshake", data) this.emitReserved("handshake", data)
this.id = data.sid this.id = data.sid
this.transport.query.sid = data.sid this.transport.query.sid = data.sid
this.upgrades = this.filterUpgrades(data.upgrades) this.upgrades = this.filterUpgrades(data.upgrades)
this.pingInterval = data.pingInterval this.pingInterval = data.pingInterval
this.pingTimeout = data.pingTimeout this.pingTimeout = data.pingTimeout
this.maxPayload = data.maxPayload
this.onOpen() this.onOpen()
// In case open handler closes socket // In case open handler closes socket
if ("closed" === this.readyState) return if ("closed" === this.readyState) return
@@ -437,7 +710,7 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
resetPingTimeout() { private resetPingTimeout() {
this.clearTimeoutFn(this.pingTimeoutTimer) this.clearTimeoutFn(this.pingTimeoutTimer)
this.pingTimeoutTimer = this.setTimeoutFn(() => { this.pingTimeoutTimer = this.setTimeoutFn(() => {
this.onClose("ping timeout") this.onClose("ping timeout")
@@ -452,7 +725,7 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
onDrain() { private onDrain() {
this.writeBuffer.splice(0, this.prevBufferLen) this.writeBuffer.splice(0, this.prevBufferLen)
// setting prevBufferLen = 0 is very important // setting prevBufferLen = 0 is very important
@@ -461,7 +734,7 @@ export class Socket extends Emitter {
this.prevBufferLen = 0 this.prevBufferLen = 0
if (0 === this.writeBuffer.length) { if (0 === this.writeBuffer.length) {
this.emit("drain") this.emitReserved("drain")
} else { } else {
this.flush() this.flush()
} }
@@ -472,22 +745,53 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
flush() { private flush() {
if ( if (
"closed" !== this.readyState && "closed" !== this.readyState &&
this.transport.writable && this.transport.writable &&
!this.upgrading && !this.upgrading &&
this.writeBuffer.length this.writeBuffer.length
) { ) {
debug("flushing %d packets in socket", this.writeBuffer.length) const packets = this.getWritablePackets()
this.transport.send(this.writeBuffer) debug("flushing %d packets in socket", packets.length)
this.transport.send(packets)
// keep track of current length of writeBuffer // keep track of current length of writeBuffer
// splice writeBuffer and callbackBuffer on `drain` // splice writeBuffer and callbackBuffer on `drain`
this.prevBufferLen = this.writeBuffer.length this.prevBufferLen = packets.length
this.emit("flush") this.emitReserved("flush")
} }
} }
/**
* Ensure the encoded size of the writeBuffer is below the maxPayload value sent by the server (only for HTTP
* long-polling)
*
* @private
*/
private getWritablePackets() {
const shouldCheckPayloadSize =
this.maxPayload &&
this.transport.name === "polling" &&
this.writeBuffer.length > 1
if (!shouldCheckPayloadSize) {
return this.writeBuffer
}
let payloadSize = 1 // first packet type
for (let i = 0; i < this.writeBuffer.length; i++) {
const data = this.writeBuffer[i].data
if (data) {
payloadSize += byteLength(data)
}
if (i > 0 && payloadSize > this.maxPayload) {
debug("only send %d out of %d packets", i, this.writeBuffer.length)
return this.writeBuffer.slice(0, i)
}
payloadSize += 2 // separator + packet type
}
debug("payload size is %d (max: %d)", payloadSize, this.maxPayload)
return this.writeBuffer
}
/** /**
* Sends a message. * Sends a message.
* *
@@ -497,12 +801,12 @@ export class Socket extends Emitter {
* @return {Socket} for chaining. * @return {Socket} for chaining.
* @api public * @api public
*/ */
write(msg, options, fn) { public write(msg, options, fn?) {
this.sendPacket("message", msg, options, fn) this.sendPacket("message", msg, options, fn)
return this return this
} }
send(msg, options, fn) { public send(msg, options, fn?) {
this.sendPacket("message", msg, options, fn) this.sendPacket("message", msg, options, fn)
return this return this
} }
@@ -516,7 +820,7 @@ export class Socket extends Emitter {
* @param {Function} callback function. * @param {Function} callback function.
* @api private * @api private
*/ */
sendPacket(type, data?, options?, fn?) { private sendPacket(type, data?, options?, fn?) {
if ("function" === typeof data) { if ("function" === typeof data) {
fn = data fn = data
data = undefined data = undefined
@@ -539,7 +843,7 @@ export class Socket extends Emitter {
data: data, data: data,
options: options options: options
} }
this.emit("packetCreate", packet) this.emitReserved("packetCreate", packet)
this.writeBuffer.push(packet) this.writeBuffer.push(packet)
if (fn) this.once("flush", fn) if (fn) this.once("flush", fn)
this.flush() this.flush()
@@ -548,9 +852,9 @@ export class Socket extends Emitter {
/** /**
* Closes the connection. * Closes the connection.
* *
* @api private * @api public
*/ */
close() { public close() {
const close = () => { const close = () => {
this.onClose("forced close") this.onClose("forced close")
debug("socket closing - telling transport to close") debug("socket closing - telling transport to close")
@@ -558,8 +862,8 @@ export class Socket extends Emitter {
} }
const cleanupAndClose = () => { const cleanupAndClose = () => {
this.removeListener("upgrade", cleanupAndClose) this.off("upgrade", cleanupAndClose)
this.removeListener("upgradeError", cleanupAndClose) this.off("upgradeError", cleanupAndClose)
close() close()
} }
@@ -595,10 +899,10 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
onError(err) { private onError(err) {
debug("socket error %j", err) debug("socket error %j", err)
Socket.priorWebsocketSuccess = false Socket.priorWebsocketSuccess = false
this.emit("error", err) this.emitReserved("error", err)
this.onClose("transport error", err) this.onClose("transport error", err)
} }
@@ -607,7 +911,7 @@ export class Socket extends Emitter {
* *
* @api private * @api private
*/ */
onClose(reason, desc?) { private onClose(reason: string, description?: CloseDetails | Error) {
if ( if (
"opening" === this.readyState || "opening" === this.readyState ||
"open" === this.readyState || "open" === this.readyState ||
@@ -616,7 +920,6 @@ export class Socket extends Emitter {
debug('socket close with reason: "%s"', reason) debug('socket close with reason: "%s"', reason)
// clear timers // clear timers
this.clearTimeoutFn(this.pingIntervalTimer)
this.clearTimeoutFn(this.pingTimeoutTimer) this.clearTimeoutFn(this.pingTimeoutTimer)
// stop event from firing again for transport // stop event from firing again for transport
@@ -629,6 +932,11 @@ export class Socket extends Emitter {
this.transport.removeAllListeners() this.transport.removeAllListeners()
if (typeof removeEventListener === "function") { if (typeof removeEventListener === "function") {
removeEventListener(
"beforeunload",
this.beforeunloadEventListener,
false
)
removeEventListener("offline", this.offlineEventListener, false) removeEventListener("offline", this.offlineEventListener, false)
} }
@@ -639,7 +947,7 @@ export class Socket extends Emitter {
this.id = null this.id = null
// emit close event // emit close event
this.emit("close", reason, desc) this.emitReserved("close", reason, description)
// clean buffers after, so users can still // clean buffers after, so users can still
// grab the buffers on `close` event // grab the buffers on `close` event
@@ -655,7 +963,7 @@ export class Socket extends Emitter {
* @api private * @api private
* *
*/ */
filterUpgrades(upgrades) { private filterUpgrades(upgrades) {
const filteredUpgrades = [] const filteredUpgrades = []
let i = 0 let i = 0
const j = upgrades.length const j = upgrades.length
@@ -666,23 +974,3 @@ export class Socket extends Emitter {
return filteredUpgrades return filteredUpgrades
} }
} }
Socket.priorWebsocketSuccess = false
/**
* Protocol version.
*
* @api public
*/
Socket.protocol = parser.protocol // this is an int
function clone(obj) {
const o = {}
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = obj[i]
}
}
return o
}

View File

@@ -1,61 +1,103 @@
import parser from "../engine.io-parser" // import { decodePacket, Packet, RawData } from "engine.io-parser"
const Emitter = require("component-emitter") import { decodePacket, Packet, RawData } from "../engine.io-parser"
import { Emitter } from "@socket.io/component-emitter"
import { installTimerFunctions } from "./util" import { installTimerFunctions } from "./util"
const debug = (...args: any) => console.debug('engine.io-client:transport', ...args)//require("debug")("engine.io-client:transport") // import debugModule from "debug"; // debug()
import { SocketOptions } from "./socket"
// const debug = debugModule("engine.io-client:transport"); // debug()
const debug = require('../debug')("engine.io-client:transport") // debug()
class TransportError extends Error {
public readonly type = "TransportError";
constructor(
reason: string,
readonly description: any,
readonly context: any
) {
super(reason)
}
}
export interface CloseDetails {
description: string
context?: unknown // context should be typed as CloseEvent | XMLHttpRequest, but these types are not available on non-browser platforms
}
interface TransportReservedEvents {
open: () => void
error: (err: TransportError) => void
packet: (packet: Packet) => void
close: (details?: CloseDetails) => void
poll: () => void
pollComplete: () => void
drain: () => void
}
type TransportState = "opening" | "open" | "closed" | "pausing" | "paused"
export abstract class Transport extends Emitter<
Record<never, never>,
Record<never, never>,
TransportReservedEvents
> {
public query: Record<string, string>
public writable: boolean = false;
protected opts: SocketOptions
protected supportsBinary: boolean
protected readyState: TransportState
protected socket: any
protected setTimeoutFn: typeof setTimeout
export class Transport extends Emitter {
/** /**
* Transport abstract constructor. * Transport abstract constructor.
* *
* @param {Object} options. * @param {Object} opts - options
* @api private * @protected
*/ */
constructor(opts) { constructor(opts) {
super() super()
installTimerFunctions(this, opts) installTimerFunctions(this, opts)
this.opts = opts this.opts = opts
this.query = opts.query this.query = opts.query
this.readyState = ""
this.socket = opts.socket this.socket = opts.socket
} }
/** /**
* Emits an error. * Emits an error.
* *
* @param {String} str * @param {String} reason
* @param description
* @param context - the error context
* @return {Transport} for chaining * @return {Transport} for chaining
* @api public * @protected
*/ */
onError(msg, desc) { protected onError(reason: string, description: any, context?: any) {
const err: any = new Error(msg) super.emitReserved(
err.type = "TransportError" "error",
err.description = desc new TransportError(reason, description, context)
this.emit("error", err) )
return this return this
} }
/** /**
* Opens the transport. * Opens the transport.
*
* @api public
*/ */
open() { public open() {
if ("closed" === this.readyState || "" === this.readyState) { this.readyState = "opening"
this.readyState = "opening" this.doOpen()
this.doOpen()
}
return this return this
} }
/** /**
* Closes the transport. * Closes the transport.
*
* @api private
*/ */
close() { public close() {
if ("opening" === this.readyState || "open" === this.readyState) { if (this.readyState === "opening" || this.readyState === "open") {
this.doClose() this.doClose()
this.onClose() this.onClose()
} }
@@ -67,10 +109,9 @@ export class Transport extends Emitter {
* Sends multiple packets. * Sends multiple packets.
* *
* @param {Array} packets * @param {Array} packets
* @api private
*/ */
send(packets) { public send(packets) {
if ("open" === this.readyState) { if (this.readyState === "open") {
this.write(packets) this.write(packets)
} else { } else {
// this might happen if the transport was silently closed in the beforeunload event handler // this might happen if the transport was silently closed in the beforeunload event handler
@@ -81,39 +122,58 @@ export class Transport extends Emitter {
/** /**
* Called upon open * Called upon open
* *
* @api private * @protected
*/ */
onOpen() { protected onOpen() {
this.readyState = "open" this.readyState = "open"
this.writable = true this.writable = true
this.emit("open") super.emitReserved("open")
} }
/** /**
* Called with data. * Called with data.
* *
* @param {String} data * @param {String} data
* @api private * @protected
*/ */
onData(data) { protected onData(data: RawData) {
const packet = parser.decodePacket(data, this.socket.binaryType) const packet = decodePacket(data, this.socket.binaryType)
this.onPacket(packet) this.onPacket(packet)
} }
/** /**
* Called with a decoded packet. * Called with a decoded packet.
*
* @protected
*/ */
onPacket(packet) { protected onPacket(packet: Packet) {
this.emit("packet", packet) super.emitReserved("packet", packet)
} }
/** /**
* Called upon close. * Called upon close.
* *
* @api private * @protected
*/ */
onClose() { protected onClose(details?: CloseDetails) {
this.readyState = "closed" this.readyState = "closed"
this.emit("close") super.emitReserved("close", details)
} }
/**
* The name of the transport
*/
public abstract get name(): string
/**
* Pauses the transport, in order not to lose packets during an upgrade.
*
* @param onPause
*/
public pause(onPause: () => void) { }
protected abstract doOpen()
protected abstract doClose()
protected abstract write(packets: Packet[])
} }

View File

@@ -1,4 +1,5 @@
import { WS } from "./websocket" import { WS } from "./websocket.js"
export default {
'websocket': WS export const transports = {
websocket: WS,
} }

View File

@@ -0,0 +1,6 @@
import { WebSocket as ws } from "../../client"
export const WebSocket = ws
export const usingBrowserWebSocket = false
export const defaultBinaryType = "nodebuffer"
export const nextTick = process.nextTick

View File

@@ -1,21 +1,18 @@
import { Transport } from '../transport' import { Transport } from "../transport"
// const Transport = require("../transport") import { encode } from "../contrib/parseqs"
import parser from '../../engine.io-parser' import { yeast } from "../contrib/yeast"
// const parser = require("../engine.io-parser") import { pick } from "../util"
const parseqs = require("parseqs") import {
const yeast = require("yeast") defaultBinaryType,
import { pick } from '../util' nextTick,
// const { pick } = require("../util") usingBrowserWebSocket,
import { WebSocket } from '../../client' WebSocket
const usingBrowserWebSocket = true } from "./websocket-constructor"
// const { // import debugModule from "debug" // debug()
// WebSocket, import { encodePacket } from "../../engine.io-parser"
// usingBrowserWebSocket,
// defaultBinaryType,
// nextTick
// } = require("./websocket-constructor")
const debug = (...args: any) => console.debug('engine.io-client:websocket', ...args)//require("debug")("engine.io-client:websocket") // const debug = debugModule("engine.io-client:websocket") // debug()
const debug = (...args: any) => console.debug('engine.io-client:websocket', ...args)
// detect ReactNative environment // detect ReactNative environment
const isReactNative = const isReactNative =
@@ -24,11 +21,13 @@ const isReactNative =
navigator.product.toLowerCase() === "reactnative" navigator.product.toLowerCase() === "reactnative"
export class WS extends Transport { export class WS extends Transport {
private ws: any
/** /**
* WebSocket transport constructor. * WebSocket transport constructor.
* *
* @api {Object} connection options * @param {Object} opts - connection options
* @api public * @protected
*/ */
constructor(opts) { constructor(opts) {
super(opts) super(opts)
@@ -36,21 +35,11 @@ export class WS extends Transport {
this.supportsBinary = !opts.forceBase64 this.supportsBinary = !opts.forceBase64
} }
/** override get name() {
* Transport name.
*
* @api public
*/
get name() {
return "websocket" return "websocket"
} }
/** override doOpen() {
* Opens socket.
*
* @api private
*/
doOpen() {
if (!this.check()) { if (!this.check()) {
// let probe timeout // let probe timeout
return return
@@ -86,17 +75,17 @@ export class WS extends Transport {
} }
try { try {
this.ws = new WebSocket(uri, protocols) this.ws =
// usingBrowserWebSocket && !isReactNative usingBrowserWebSocket && !isReactNative
// ? protocols ? protocols
// ? new WebSocket(uri, protocols) ? new WebSocket(uri, protocols)
// : new WebSocket(uri) : new WebSocket(uri)
// : new WebSocket(uri, protocols, opts) : new WebSocket(uri, protocols, opts)
} catch (err) { } catch (err: any) {
return this.emit("error", err) return this.emitReserved("error", err)
} }
this.ws.binaryType = this.socket.binaryType || 'arraybuffer' this.ws.binaryType = this.socket.binaryType || defaultBinaryType
this.addEventListeners() this.addEventListeners()
} }
@@ -104,27 +93,25 @@ export class WS extends Transport {
/** /**
* Adds event listeners to the socket * Adds event listeners to the socket
* *
* @api private * @private
*/ */
addEventListeners() { private addEventListeners() {
this.ws.onopen = () => { this.ws.onopen = () => {
if (this.opts.autoUnref) { if (this.opts.autoUnref) {
this.ws._socket.unref() this.ws._socket.unref()
} }
this.onOpen() this.onOpen()
} }
this.ws.onclose = this.onClose.bind(this) this.ws.onclose = (closeEvent) =>
this.ws.onmessage = ev => this.onData(ev.data) this.onClose({
this.ws.onerror = e => this.onError("websocket error", e) description: "websocket connection closed",
context: closeEvent,
})
this.ws.onmessage = (ev) => this.onData(ev.data)
this.ws.onerror = (e) => this.onError("websocket error", e)
} }
/** override write(packets) {
* Writes data to socket.
*
* @param {Array} array of packets.
* @api private
*/
write(packets) {
this.writable = false this.writable = false
// encodePacket efficient as it uses WS framing // encodePacket efficient as it uses WS framing
@@ -133,9 +120,9 @@ export class WS extends Transport {
const packet = packets[i] const packet = packets[i]
const lastPacket = i === packets.length - 1 const lastPacket = i === packets.length - 1
parser.encodePacket(packet, this.supportsBinary, data => { encodePacket(packet, this.supportsBinary, (data) => {
// always create a new object (GH-437) // always create a new object (GH-437)
const opts: any = {} const opts: { compress?: boolean } = {}
if (!usingBrowserWebSocket) { if (!usingBrowserWebSocket) {
if (packet.options) { if (packet.options) {
opts.compress = packet.options.compress opts.compress = packet.options.compress
@@ -143,6 +130,7 @@ export class WS extends Transport {
if (this.opts.perMessageDeflate) { if (this.opts.perMessageDeflate) {
const len = const len =
// @ts-ignore
"string" === typeof data ? Buffer.byteLength(data) : data.length "string" === typeof data ? Buffer.byteLength(data) : data.length
if (len < this.opts.perMessageDeflate.threshold) { if (len < this.opts.perMessageDeflate.threshold) {
opts.compress = false opts.compress = false
@@ -160,37 +148,23 @@ export class WS extends Transport {
} else { } else {
this.ws.send(data, opts) this.ws.send(data, opts)
} }
} catch (error: any) { } catch (e) {
debug("websocket closed before onclose event") debug("websocket closed before onclose event")
} }
if (lastPacket) { if (lastPacket) {
// fake drain // fake drain
// defer to next tick to allow Socket to clear writeBuffer // defer to next tick to allow Socket to clear writeBuffer
process.nextTick(() => { nextTick(() => {
this.writable = true this.writable = true
this.emit("drain") this.emitReserved("drain")
}, this.setTimeoutFn) }, this.setTimeoutFn)
} }
}) })
} }
} }
/** override doClose() {
* Called upon close
*
* @api private
*/
onClose() {
Transport.prototype.onClose.call(this)
}
/**
* Closes socket.
*
* @api private
*/
doClose() {
if (typeof this.ws !== "undefined") { if (typeof this.ws !== "undefined") {
this.ws.close() this.ws.close()
this.ws = null this.ws = null
@@ -200,10 +174,10 @@ export class WS extends Transport {
/** /**
* Generates uri for connection. * Generates uri for connection.
* *
* @api private * @private
*/ */
uri() { uri() {
let query = this.query || {} let query: { b64?: number } = this.query || {}
const schema = this.opts.secure ? "wss" : "ws" const schema = this.opts.secure ? "wss" : "ws"
let port = "" let port = ""
@@ -226,21 +200,16 @@ export class WS extends Transport {
query.b64 = 1 query.b64 = 1
} }
query = parseqs.encode(query) const encodedQuery = encode(query)
// prepend ? to query
if (query.length) {
query = "?" + query
}
const ipv6 = this.opts.hostname.indexOf(":") !== -1 const ipv6 = this.opts.hostname.indexOf(":") !== -1
return ( return (
schema + schema +
"://" + "://" +
(ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) + (ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) +
port + port +
this.opts.path + this.opts.path +
query (encodedQuery.length ? "?" + encodedQuery : "")
) )
} }
@@ -248,12 +217,9 @@ export class WS extends Transport {
* Feature detection for WebSocket. * Feature detection for WebSocket.
* *
* @return {Boolean} whether this transport is available. * @return {Boolean} whether this transport is available.
* @api public * @private
*/ */
check() { private check() {
return ( return !!WebSocket
!!WebSocket &&
!("__initialize" in WebSocket && this.name === WS.prototype.name)
)
} }
} }

View File

@@ -1,4 +1,6 @@
const pick = (obj, ...attr) => { // import { globalThisShim as globalThis } from "./globalThis.js"
export function pick(obj, ...attr) {
return attr.reduce((acc, k) => { return attr.reduce((acc, k) => {
if (obj.hasOwnProperty(k)) { if (obj.hasOwnProperty(k)) {
acc[k] = obj[k] acc[k] = obj[k]
@@ -8,16 +10,46 @@ const pick = (obj, ...attr) => {
} }
// Keep a reference to the real timeout functions so they can be used when overridden // Keep a reference to the real timeout functions so they can be used when overridden
const NATIVE_SET_TIMEOUT = setTimeout const NATIVE_SET_TIMEOUT = globalThis.setTimeout
const NATIVE_CLEAR_TIMEOUT = clearTimeout const NATIVE_CLEAR_TIMEOUT = globalThis.clearTimeout
const installTimerFunctions = (obj, opts) => { export function installTimerFunctions(obj, opts) {
if (opts.useNativeTimers) { if (opts.useNativeTimers) {
obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThis) obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThis)
obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThis) obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThis)
} else { } else {
obj.setTimeoutFn = setTimeout.bind(globalThis) obj.setTimeoutFn = globalThis.setTimeout.bind(globalThis)
obj.clearTimeoutFn = clearTimeout.bind(globalThis) obj.clearTimeoutFn = globalThis.clearTimeout.bind(globalThis)
} }
} }
export { pick, installTimerFunctions }
// base64 encoded buffers are about 33% bigger (https://en.wikipedia.org/wiki/Base64)
const BASE64_OVERHEAD = 1.33
// we could also have used `new Blob([obj]).size`, but it isn't supported in IE9
export function byteLength(obj) {
if (typeof obj === "string") {
return utf8Length(obj)
}
// arraybuffer or blob
return Math.ceil((obj.byteLength || obj.size) * BASE64_OVERHEAD)
}
function utf8Length(str) {
let c = 0,
length = 0
for (let i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i)
if (c < 0x80) {
length += 1
} else if (c < 0x800) {
length += 2
} else if (c < 0xd800 || c >= 0xe000) {
length += 3
} else {
i++
length += 4
}
}
return length
}

View File

@@ -1,21 +1,39 @@
const PACKET_TYPES = Object.create(null) // no Map = no polyfill const PACKET_TYPES = Object.create(null); // no Map = no polyfill
PACKET_TYPES["open"] = "0" PACKET_TYPES["open"] = "0";
PACKET_TYPES["close"] = "1" PACKET_TYPES["close"] = "1";
PACKET_TYPES["ping"] = "2" PACKET_TYPES["ping"] = "2";
PACKET_TYPES["pong"] = "3" PACKET_TYPES["pong"] = "3";
PACKET_TYPES["message"] = "4" PACKET_TYPES["message"] = "4";
PACKET_TYPES["upgrade"] = "5" PACKET_TYPES["upgrade"] = "5";
PACKET_TYPES["noop"] = "6" PACKET_TYPES["noop"] = "6";
const PACKET_TYPES_REVERSE = Object.create(null) const PACKET_TYPES_REVERSE = Object.create(null);
Object.keys(PACKET_TYPES).forEach(key => { Object.keys(PACKET_TYPES).forEach(key => {
PACKET_TYPES_REVERSE[PACKET_TYPES[key]] = key PACKET_TYPES_REVERSE[PACKET_TYPES[key]] = key;
}) });
const ERROR_PACKET = { type: "error", data: "parser error" } const ERROR_PACKET: Packet = { type: "error", data: "parser error" };
export = { export { PACKET_TYPES, PACKET_TYPES_REVERSE, ERROR_PACKET };
PACKET_TYPES,
PACKET_TYPES_REVERSE, export type PacketType =
ERROR_PACKET | "open"
| "close"
| "ping"
| "pong"
| "message"
| "upgrade"
| "noop"
| "error";
// RawData should be "string | Buffer | ArrayBuffer | ArrayBufferView | Blob", but Blob does not exist in Node.js and
// requires to add the dom lib in tsconfig.json
export type RawData = any;
export interface Packet {
type: PacketType;
options?: { compress: boolean };
data?: RawData;
} }
export type BinaryType = "nodebuffer" | "arraybuffer" | "blob";

View File

@@ -1,48 +1,60 @@
const { PACKET_TYPES_REVERSE, ERROR_PACKET } = require("./commons") import {
ERROR_PACKET,
PACKET_TYPES_REVERSE,
Packet,
BinaryType,
RawData
} from "./commons.js";
export const decodePacket = (encodedPacket, binaryType) => { const decodePacket = (
if (typeof encodedPacket !== "string") { encodedPacket: RawData,
return { binaryType?: BinaryType
type: "message", ): Packet => {
data: mapBinary(encodedPacket, binaryType) if (typeof encodedPacket !== "string") {
} return {
} type: "message",
const type = encodedPacket.charAt(0) data: mapBinary(encodedPacket, binaryType)
if (type === "b") { };
const buffer = Buffer.from(encodedPacket.substring(1), "base64") }
return { const type = encodedPacket.charAt(0);
type: "message", if (type === "b") {
data: mapBinary(buffer, binaryType) const buffer = Buffer.from(encodedPacket.substring(1), "base64");
} return {
} type: "message",
if (!PACKET_TYPES_REVERSE[type]) { data: mapBinary(buffer, binaryType)
return ERROR_PACKET };
} }
return encodedPacket.length > 1 if (!PACKET_TYPES_REVERSE[type]) {
? { return ERROR_PACKET;
type: PACKET_TYPES_REVERSE[type], }
data: encodedPacket.substring(1) return encodedPacket.length > 1
} ? {
: { type: PACKET_TYPES_REVERSE[type],
type: PACKET_TYPES_REVERSE[type] data: encodedPacket.substring(1)
} }
} : {
type: PACKET_TYPES_REVERSE[type]
};
};
const mapBinary = (data, binaryType) => { const mapBinary = (data: RawData, binaryType?: BinaryType) => {
switch (binaryType) { const isBuffer = Buffer.isBuffer(data);
case "arraybuffer": switch (binaryType) {
return Buffer.isBuffer(data) ? toArrayBuffer(data) : data case "arraybuffer":
case "nodebuffer": return isBuffer ? toArrayBuffer(data) : data;
default: case "nodebuffer":
return data // assuming the data is already a Buffer default:
} return data; // assuming the data is already a Buffer
} }
};
const toArrayBuffer = buffer => { const toArrayBuffer = (buffer: Buffer): ArrayBuffer => {
const arrayBuffer = new ArrayBuffer(buffer.length) const arrayBuffer = new ArrayBuffer(buffer.length);
const view = new Uint8Array(arrayBuffer) const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < buffer.length; i++) { for (let i = 0; i < buffer.length; i++) {
view[i] = buffer[i] view[i] = buffer[i];
} }
return arrayBuffer return arrayBuffer;
} };
export default decodePacket;

View File

@@ -1,26 +1,31 @@
const { PACKET_TYPES } = require("./commons") import { PACKET_TYPES, Packet, RawData } from "./commons.js";
export const encodePacket = ({ type, data }, supportsBinary, callback) => { const encodePacket = (
console.trace('encodePacket', type, JSON.stringify(data)) { type, data }: Packet,
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) { supportsBinary: boolean,
const buffer = toBuffer(data) callback: (encodedPacket: RawData) => void
return callback(encodeBuffer(buffer, supportsBinary)) ) => {
} if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
// plain string const buffer = toBuffer(data);
return callback(PACKET_TYPES[type] + (data || "")) return callback(encodeBuffer(buffer, supportsBinary));
} }
// plain string
return callback(PACKET_TYPES[type] + (data || ""));
};
const toBuffer = data => { const toBuffer = data => {
if (Buffer.isBuffer(data)) { if (Buffer.isBuffer(data)) {
return data return data;
} else if (data instanceof ArrayBuffer) { } else if (data instanceof ArrayBuffer) {
return Buffer.from(data) return Buffer.from(data);
} else { } else {
return Buffer.from(data.buffer, data.byteOffset, data.byteLength) return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
} }
} };
// only 'message' packets can contain binary, so the type prefix is not needed // only 'message' packets can contain binary, so the type prefix is not needed
const encodeBuffer = (data, supportsBinary) => { const encodeBuffer = (data: Buffer, supportsBinary: boolean): RawData => {
return supportsBinary ? data : "b" + data.toString("base64") return supportsBinary ? data : "b" + data.toString("base64");
} };
export default encodePacket;

View File

@@ -1,42 +1,53 @@
import { encodePacket } from "./encodePacket" import encodePacket from "./encodePacket.js";
import { decodePacket } from "./decodePacket" import decodePacket from "./decodePacket.js";
import { Packet, PacketType, RawData, BinaryType } from "./commons.js";
const SEPARATOR = String.fromCharCode(30) // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text const SEPARATOR = String.fromCharCode(30); // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text
const encodePayload = (packets, callback) => { const encodePayload = (
// some packets may be added to the array while encoding, so the initial length must be saved packets: Packet[],
const length = packets.length callback: (encodedPayload: string) => void
const encodedPackets = new Array(length) ) => {
let count = 0 // some packets may be added to the array while encoding, so the initial length must be saved
const length = packets.length;
const encodedPackets = new Array(length);
let count = 0;
packets.forEach((packet, i) => { packets.forEach((packet, i) => {
// force base64 encoding for binary packets // force base64 encoding for binary packets
encodePacket(packet, false, encodedPacket => { encodePacket(packet, false, encodedPacket => {
encodedPackets[i] = encodedPacket encodedPackets[i] = encodedPacket;
if (++count === length) { if (++count === length) {
callback(encodedPackets.join(SEPARATOR)) callback(encodedPackets.join(SEPARATOR));
} }
}) });
}) });
} };
const decodePayload = (encodedPayload, binaryType) => { const decodePayload = (
const encodedPackets = encodedPayload.split(SEPARATOR) encodedPayload: string,
const packets = [] binaryType?: BinaryType
for (let i = 0; i < encodedPackets.length; i++) { ): Packet[] => {
const decodedPacket = decodePacket(encodedPackets[i], binaryType) const encodedPackets = encodedPayload.split(SEPARATOR);
packets.push(decodedPacket) const packets = [];
if (decodedPacket.type === "error") { for (let i = 0; i < encodedPackets.length; i++) {
break const decodedPacket = decodePacket(encodedPackets[i], binaryType);
} packets.push(decodedPacket);
if (decodedPacket.type === "error") {
break;
} }
return packets }
} return packets;
};
export default { export const protocol = 4;
protocol: 4, export {
encodePacket, encodePacket,
encodePayload, encodePayload,
decodePacket, decodePacket,
decodePayload decodePayload,
} Packet,
PacketType,
RawData,
BinaryType
};

View File

@@ -1,10 +1,45 @@
/** // import { createServer } from "http"
* Module dependencies. import { Server, AttachOptions, ServerOptions } from "./server";
*/ import transports from "./transports/index";
import * as parser from "../engine.io-parser";
// const http = require("http") // export { Server, transports, listen, attach, parser }
// const Server = require("./server") export { Server, transports, attach, parser };
import { Server } from './server' export { AttachOptions, ServerOptions } from "./server";
// export { uServer } from "./userver";
export { Socket } from "./socket";
export { Transport } from "./transport";
export const protocol = parser.protocol;
// /**
// * Creates an http.Server exclusively used for WS upgrades.
// *
// * @param {Number} port
// * @param {Function} callback
// * @param {Object} options
// * @return {Server} websocket.io server
// * @api public
// */
//
// function listen(port, options: AttachOptions & ServerOptions, fn) {
// if ("function" === typeof options) {
// fn = options;
// options = {};
// }
// const server = createServer(function(req, res) {
// res.writeHead(501);
// res.end("Not Implemented");
// });
// // create engine server
// const engine = attach(server, options);
// engine.httpServer = server;
// server.listen(port, fn);
// return engine;
// }
/** /**
* Captures upgrade requests for a http.Server. * Captures upgrade requests for a http.Server.
@@ -15,12 +50,8 @@ import { Server } from './server'
* @api public * @api public
*/ */
function attach(srv, options) { function attach(server, options: AttachOptions & ServerOptions) {
const engine = new Server(options) const engine = new Server(options);
engine.attach(srv, options) engine.attach(server, options);
return engine return engine;
}
export = {
attach
} }

View File

@@ -0,0 +1,485 @@
// imported from https://github.com/socketio/engine.io-parser/tree/2.2.x
/**
* Module dependencies.
*/
var utf8 = require('./utf8');
/**
* Current protocol version.
*/
export const protocol = 3;
const hasBinary = (packets) => {
for (const packet of packets) {
if (packet.data instanceof ArrayBuffer || ArrayBuffer.isView(packet.data)) {
return true;
}
}
return false;
}
/**
* Packet types.
*/
export const packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = Object.keys(packets);
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
const EMPTY_BUFFER = Buffer.concat([]);
/**
* Encodes a packet.
*
* <packet type id> [ <data> ]
*
* Example:
*
* 5hello world
* 3
* 4
*
* Binary is encoded in an identical principle
*
* @api private
*/
export function encodePacket (packet, supportsBinary, utf8encode, callback) {
if (typeof supportsBinary === 'function') {
callback = supportsBinary;
supportsBinary = null;
}
if (typeof utf8encode === 'function') {
callback = utf8encode;
utf8encode = null;
}
if (Buffer.isBuffer(packet.data)) {
return encodeBuffer(packet, supportsBinary, callback);
} else if (packet.data && (packet.data.buffer || packet.data) instanceof ArrayBuffer) {
return encodeBuffer({ type: packet.type, data: arrayBufferToBuffer(packet.data) }, supportsBinary, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data);
}
return callback('' + encoded);
};
/**
* Encode Buffer data
*/
function encodeBuffer(packet, supportsBinary, callback) {
if (!supportsBinary) {
return encodeBase64Packet(packet, callback);
}
var data = packet.data;
var typeBuffer = Buffer.allocUnsafe(1);
typeBuffer[0] = packets[packet.type];
return callback(Buffer.concat([typeBuffer, data]));
}
/**
* Encodes a packet with binary data in a base64 string
*
* @param {Object} packet, has `type` and `data`
* @return {String} base64 encoded message
*/
export function encodeBase64Packet (packet, callback){
var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data);
var message = 'b' + packets[packet.type];
message += data.toString('base64');
return callback(message);
};
/**
* Decodes a packet. Data also available as an ArrayBuffer if requested.
*
* @return {Object} with `type` and `data` (if any)
* @api private
*/
export function decodePacket (data, binaryType, utf8decode) {
if (data === undefined) {
return err;
}
var type;
// String data
if (typeof data === 'string') {
type = data.charAt(0);
if (type === 'b') {
return decodeBase64Packet(data.substr(1), binaryType);
}
if (utf8decode) {
data = tryDecode(data);
if (data === false) {
return err;
}
}
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
// Binary data
if (binaryType === 'arraybuffer') {
// wrap Buffer/ArrayBuffer data into an Uint8Array
var intArray = new Uint8Array(data);
type = intArray[0];
return { type: packetslist[type], data: intArray.buffer.slice(1) };
}
if (data instanceof ArrayBuffer) {
data = arrayBufferToBuffer(data);
}
type = data[0];
return { type: packetslist[type], data: data.slice(1) };
};
function tryDecode(data) {
try {
data = utf8.decode(data, { strict: false });
} catch (e) {
return false;
}
return data;
}
/**
* Decodes a packet encoded in a base64 string.
*
* @param {String} base64 encoded message
* @return {Object} with `type` and `data` (if any)
*/
export function decodeBase64Packet (msg, binaryType) {
var type = packetslist[msg.charAt(0)];
var data = Buffer.from(msg.substr(1), 'base64');
if (binaryType === 'arraybuffer') {
var abv = new Uint8Array(data.length);
for (var i = 0; i < abv.length; i++){
abv[i] = data[i];
}
// @ts-ignore
data = abv.buffer;
}
return { type: type, data: data };
};
/**
* Encodes multiple messages (payload).
*
* <length>:data
*
* Example:
*
* 11:hello world2:hi
*
* If any contents are binary, they will be encoded as base64 strings. Base64
* encoded strings are marked with a b before the length specifier
*
* @param {Array} packets
* @api private
*/
export function encodePayload (packets, supportsBinary, callback) {
if (typeof supportsBinary === 'function') {
callback = supportsBinary;
supportsBinary = null;
}
if (supportsBinary && hasBinary(packets)) {
return encodePayloadAsBinary(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function encodeOne(packet, doneCallback) {
encodePacket(packet, supportsBinary, false, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
function setLengthHeader(message) {
return message.length + ':' + message;
}
/**
* Async array map using after
*/
function map(ary, each, done) {
const results = new Array(ary.length);
let count = 0;
for (let i = 0; i < ary.length; i++) {
each(ary[i], (error, msg) => {
results[i] = msg;
if (++count === ary.length) {
done(null, results);
}
});
}
}
/*
* Decodes data when a payload is maybe expected. Possible binary contents are
* decoded from their base64 representation
*
* @param {String} data, callback method
* @api public
*/
export function decodePayload (data, binaryType, callback) {
if (typeof data !== 'string') {
return decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
if (data === '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = '', n, msg, packet;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (chr !== ':') {
length += chr;
continue;
}
// @ts-ignore
if (length === '' || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = decodePacket(msg, binaryType, false);
if (err.type === packet.type && err.data === packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var more = callback(packet, i + n, l);
if (false === more) return;
}
// advance cursor
i += n;
length = '';
}
if (length !== '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
/**
*
* Converts a buffer to a utf8.js encoded string
*
* @api private
*/
function bufferToString(buffer) {
var str = '';
for (var i = 0, l = buffer.length; i < l; i++) {
str += String.fromCharCode(buffer[i]);
}
return str;
}
/**
*
* Converts a utf8.js encoded string to a buffer
*
* @api private
*/
function stringToBuffer(string) {
var buf = Buffer.allocUnsafe(string.length);
for (var i = 0, l = string.length; i < l; i++) {
buf.writeUInt8(string.charCodeAt(i), i);
}
return buf;
}
/**
*
* Converts an ArrayBuffer to a Buffer
*
* @api private
*/
function arrayBufferToBuffer(data) {
// data is either an ArrayBuffer or ArrayBufferView.
var length = data.byteLength || data.length;
var offset = data.byteOffset || 0;
return Buffer.from(data.buffer || data, offset, length);
}
/**
* Encodes multiple messages (payload) as binary.
*
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
* 255><data>
*
* Example:
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
*
* @param {Array} packets
* @return {Buffer} encoded payload
* @api private
*/
export function encodePayloadAsBinary (packets, callback) {
if (!packets.length) {
return callback(EMPTY_BUFFER);
}
map(packets, encodeOneBinaryPacket, function(err, results) {
return callback(Buffer.concat(results));
});
};
function encodeOneBinaryPacket(p, doneCallback) {
function onBinaryPacketEncode(packet) {
var encodingLength = '' + packet.length;
var sizeBuffer;
if (typeof packet === 'string') {
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
sizeBuffer[0] = 0; // is a string (not true binary = 0)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
}
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
sizeBuffer[0] = 1; // is binary (true binary = 1)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
doneCallback(null, Buffer.concat([sizeBuffer, packet]));
}
encodePacket(p, true, true, onBinaryPacketEncode);
}
/*
* Decodes data when a payload is maybe expected. Strings are decoded by
* interpreting each byte as a key code for entries marked to start with 0. See
* description of encodePayloadAsBinary
* @param {Buffer} data, callback method
* @api public
*/
export function decodePayloadAsBinary (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
var i;
while (bufferTail.length > 0) {
var strLen = '';
var isString = bufferTail[0] === 0;
for (i = 1; ; i++) {
if (bufferTail[i] === 255) break;
// 310 = char length of Number.MAX_VALUE
if (strLen.length > 310) {
return callback(err, 0, 1);
}
strLen += '' + bufferTail[i];
}
bufferTail = bufferTail.slice(strLen.length + 1);
var msgLength = parseInt(strLen, 10);
var msg = bufferTail.slice(1, msgLength + 1);
if (isString) msg = bufferToString(msg);
buffers.push(msg);
bufferTail = bufferTail.slice(msgLength + 1);
}
var total = buffers.length;
for (i = 0; i < total; i++) {
var buffer = buffers[i];
callback(decodePacket(buffer, binaryType, true), i, total);
}
};

View File

@@ -0,0 +1,210 @@
/*! https://mths.be/utf8js v2.1.2 by @mathias */
var stringFromCharCode = String.fromCharCode;
// Taken from https://mths.be/punycode
function ucs2decode(string) {
var output = [];
var counter = 0;
var length = string.length;
var value;
var extra;
while (counter < length) {
value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// high surrogate, and there is a next character
extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) { // low surrogate
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
// Taken from https://mths.be/punycode
function ucs2encode(array) {
var length = array.length;
var index = -1;
var value;
var output = '';
while (++index < length) {
value = array[index];
if (value > 0xFFFF) {
value -= 0x10000;
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
value = 0xDC00 | value & 0x3FF;
}
output += stringFromCharCode(value);
}
return output;
}
function checkScalarValue(codePoint, strict) {
if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
if (strict) {
throw Error(
'Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
' is not a scalar value'
);
}
return false;
}
return true;
}
/*--------------------------------------------------------------------------*/
function createByte(codePoint, shift) {
return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
}
function encodeCodePoint(codePoint, strict) {
if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
return stringFromCharCode(codePoint);
}
var symbol = '';
if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
}
else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
if (!checkScalarValue(codePoint, strict)) {
codePoint = 0xFFFD;
}
symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
symbol += createByte(codePoint, 6);
}
else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
symbol += createByte(codePoint, 12);
symbol += createByte(codePoint, 6);
}
symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
return symbol;
}
function utf8encode(string, opts) {
opts = opts || {};
var strict = false !== opts.strict;
var codePoints = ucs2decode(string);
var length = codePoints.length;
var index = -1;
var codePoint;
var byteString = '';
while (++index < length) {
codePoint = codePoints[index];
byteString += encodeCodePoint(codePoint, strict);
}
return byteString;
}
/*--------------------------------------------------------------------------*/
function readContinuationByte() {
if (byteIndex >= byteCount) {
throw Error('Invalid byte index');
}
var continuationByte = byteArray[byteIndex] & 0xFF;
byteIndex++;
if ((continuationByte & 0xC0) == 0x80) {
return continuationByte & 0x3F;
}
// If we end up here, its not a continuation byte
throw Error('Invalid continuation byte');
}
function decodeSymbol(strict) {
var byte1;
var byte2;
var byte3;
var byte4;
var codePoint;
if (byteIndex > byteCount) {
throw Error('Invalid byte index');
}
if (byteIndex == byteCount) {
return false;
}
// Read first byte
byte1 = byteArray[byteIndex] & 0xFF;
byteIndex++;
// 1-byte sequence (no continuation bytes)
if ((byte1 & 0x80) == 0) {
return byte1;
}
// 2-byte sequence
if ((byte1 & 0xE0) == 0xC0) {
byte2 = readContinuationByte();
codePoint = ((byte1 & 0x1F) << 6) | byte2;
if (codePoint >= 0x80) {
return codePoint;
} else {
throw Error('Invalid continuation byte');
}
}
// 3-byte sequence (may include unpaired surrogates)
if ((byte1 & 0xF0) == 0xE0) {
byte2 = readContinuationByte();
byte3 = readContinuationByte();
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
if (codePoint >= 0x0800) {
return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD;
} else {
throw Error('Invalid continuation byte');
}
}
// 4-byte sequence
if ((byte1 & 0xF8) == 0xF0) {
byte2 = readContinuationByte();
byte3 = readContinuationByte();
byte4 = readContinuationByte();
codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) |
(byte3 << 0x06) | byte4;
if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
return codePoint;
}
}
throw Error('Invalid UTF-8 detected');
}
var byteArray;
var byteCount;
var byteIndex;
function utf8decode(byteString, opts) {
opts = opts || {};
var strict = false !== opts.strict;
byteArray = ucs2decode(byteString);
byteCount = byteArray.length;
byteIndex = 0;
var codePoints = [];
var tmp;
while ((tmp = decodeSymbol(strict)) !== false) {
codePoints.push(tmp);
}
return ucs2encode(codePoints);
}
module.exports = {
version: '2.1.2',
encode: utf8encode,
decode: utf8decode
};

File diff suppressed because it is too large Load Diff

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