diff --git a/packages/api/src/console.ts b/packages/api/src/console.ts index ef0351a4..4ac75db7 100644 --- a/packages/api/src/console.ts +++ b/packages/api/src/console.ts @@ -133,6 +133,7 @@ export class MiaoScriptConsole implements Console { } } stack(ex: Error, color: boolean = true): string[] { + if (!ex) return [] let stack = ex.getStackTrace() let cache = [(color ? '§c' : '') + ex] //@ts-ignore diff --git a/packages/api/src/task.ts b/packages/api/src/task.ts index a6c91671..da4d2dca 100644 --- a/packages/api/src/task.ts +++ b/packages/api/src/task.ts @@ -1,34 +1,66 @@ import { plugin } from './index' import { injectable } from '@ccms/container' +const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") + export namespace task { @injectable() export abstract class TaskManager { - protected cacheTasks = new Map() + protected taskId: java.util.concurrent.atomic.AtomicInteger + protected cacheTasks = new Map() + protected pluginCacheTasks = new Map>() + + constructor() { + this.taskId = new AtomicInteger(0) + process.on('task.finish', (task: task.Task) => { + let taskId = task.getTaskId() + this.cacheTasks.delete(taskId) + let ownerName = task.getOwner()?.description.name + if (ownerName && this.pluginCacheTasks.has(ownerName)) { + this.pluginCacheTasks.get(ownerName).delete(taskId) + } + }) + } protected pluginCreate(plugin: plugin.Plugin, task: task.Task) { - if (!this.cacheTasks.has(plugin.description.name)) { - this.cacheTasks.set(plugin.description.name, []) + if (!this.pluginCacheTasks.has(plugin.description.name)) { + this.pluginCacheTasks.set(plugin.description.name, new Map()) } - this.cacheTasks.get(plugin.description.name).push(task) + this.pluginCacheTasks.get(plugin.description.name).set(task.getTaskId(), task) return task } + protected pluginDisable(plugin: plugin.Plugin) { - if (this.cacheTasks.has(plugin.description.name)) { - this.cacheTasks.get(plugin.description.name).forEach(task => task.cancel()) - this.cacheTasks.delete(plugin.description.name) + if (this.pluginCacheTasks.has(plugin.description.name)) { + this.pluginCacheTasks.get(plugin.description.name).forEach((task) => { + task.cancel() + }) + this.pluginCacheTasks.delete(plugin.description.name) } } + + /** + * 获得自增的任务ID + */ + protected nextId() { + return this.taskId.incrementAndGet() + } + /** * 创建任务 * @param func 任务内容 */ create(func: Function, plugin?: plugin.Plugin): task.Task { if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !') }; - let task = this.create0(func) + let task = this.create0(plugin, func, this.nextId()) + this.cacheTasks.set(task.getTaskId(), task) if (plugin) { return this.pluginCreate(plugin, task) } return task } + cancel(taskId: number) { + if (!this.cacheTasks.has(taskId)) { throw new Error(`Task ${taskId} not found!`) } + this.cacheTasks.get(taskId).cancel() + } /** * 在主线程执行代码 * @param func 执行内容 @@ -41,22 +73,33 @@ export namespace task { if (plugin) { return this.pluginDisable(plugin) } this.disable0() } - protected abstract create0(func: Function): task.Task + protected abstract create0(owner: plugin.Plugin, func: Function, id: number): task.Task protected abstract disable0() } /** * 任务抽象 */ - export abstract class Task { - protected plugin: any + export abstract class Task implements Cancelable { protected func: Function protected isAsync: boolean = false; protected laterTime: number = 0; protected interval: number = 0; + protected owner: plugin.Plugin + protected taskId: number + protected innerTask: any - constructor(plugin: any, func: Function) { - this.plugin = plugin + constructor(owner: plugin.Plugin, func: Function, id: number) { + this.owner = owner this.func = func + this.taskId = id + } + + getOwner() { + return this.owner + } + + getTaskId() { + return this.taskId } /** @@ -90,12 +133,15 @@ export namespace task { * 取消任务 */ cancel(): boolean { - throw new Error("Method not implemented.") + let result = this.cancel0() + process.emit('task.finish', this) + return result } protected run(...args: any[]): void { try { this.func(...args) + !this.interval && process.emit('task.finish', this) } catch (ex) { console.console('§4插件执行任务时发生错误', ex) console.ex(ex) @@ -107,16 +153,21 @@ export namespace task { * @param args 任务参数 */ submit(...args: any[]): Cancelable { - let cancelable = this.submit0(...args) - this.cancel = cancelable.cancel - return cancelable + this.innerTask = this.submit0(...args) + return this } /** * 提交任务 * @param args 任务参数 */ - protected abstract submit0(...args: any[]): Cancelable + protected abstract submit0(...args: any[]): any + /** + * 取消任务 + */ + protected cancel0(): boolean { + return this.innerTask?.cancel() + } } /** * 返可取消的对象 diff --git a/packages/bukkit/src/task.ts b/packages/bukkit/src/task.ts index 4461b9d5..81b3c2c7 100644 --- a/packages/bukkit/src/task.ts +++ b/packages/bukkit/src/task.ts @@ -7,17 +7,14 @@ const Callable = Java.type('java.util.concurrent.Callable') @provideSingleton(task.TaskManager) export class BukkitTaskManager extends task.TaskManager { - @inject(plugin.PluginInstance) - private pluginInstance: any - - create0(func: Function): task.Task { - return new BukkitTask(this.pluginInstance, func) + create0(owner: plugin.Plugin, func: Function, id: number): task.Task { + return new BukkitTask(owner, func, id) } callSyncMethod(func: Function): any { - return Bukkit.getScheduler().callSyncMethod(this.pluginInstance, new Callable({ call: () => func() })).get() + return Bukkit.getScheduler().callSyncMethod(base.getInstance(), new Callable({ call: () => func() })).get() } disable0() { - Bukkit.getScheduler().cancelTasks(this.pluginInstance) + Bukkit.getScheduler().cancelTasks(base.getInstance()) } } @@ -26,9 +23,9 @@ export class BukkitTask extends task.Task { let run = new BukkitRunnable({ run: () => this.run(...args) }) let funcName = `runTask${this.interval ? 'Timer' : 'Later'}${this.isAsync ? 'Asynchronously' : ''}` if (this.interval) { - return run[funcName](this.plugin, this.laterTime, this.interval) + return run[funcName](base.getInstance(), this.laterTime, this.interval) } else { - return run[funcName](this.plugin, this.laterTime) + return run[funcName](base.getInstance(), this.laterTime) } } } diff --git a/packages/bungee/src/task.ts b/packages/bungee/src/task.ts index 8e546814..b678d541 100644 --- a/packages/bungee/src/task.ts +++ b/packages/bungee/src/task.ts @@ -6,17 +6,14 @@ let TimeUnit = Java.type('java.util.concurrent.TimeUnit') @provideSingleton(task.TaskManager) export class BungeeTaskManager extends task.TaskManager { - @inject(plugin.PluginInstance) - private pluginInstance: any - - create0(func: Function): task.Task { - return new BungeeTask(this.pluginInstance, func) + create0(owner: plugin.Plugin, func: Function, id: number): task.Task { + return new BungeeTask(owner, func, id) } callSyncMethod(func: Function): any { return func() } disable0() { - this.pluginInstance.getProxy().getScheduler().cancel(this.pluginInstance) + base.getInstance().getProxy().getScheduler().cancel(base.getInstance()) } } @@ -24,12 +21,12 @@ export class BungeeTask extends task.Task { submit0(...args: any[]): task.Cancelable { let run = new Runnable({ run: () => this.run(...args) }) if (this.isAsync) { - return this.plugin.getProxy().getScheduler().runAsync(this.plugin, run) + return base.getInstance().getProxy().getScheduler().runAsync(base.getInstance(), run) } if (this.interval) { - return this.plugin.getProxy().getScheduler().schedule(this.plugin, run, this.laterTime * 50, this.interval * 50, TimeUnit.MILLISECONDS) + return base.getInstance().getProxy().getScheduler().schedule(base.getInstance(), run, this.laterTime * 50, this.interval * 50, TimeUnit.MILLISECONDS) } else { - return this.plugin.getProxy().getScheduler().schedule(this.plugin, run, this.laterTime * 50, TimeUnit.MILLISECONDS) + return base.getInstance().getProxy().getScheduler().schedule(base.getInstance(), run, this.laterTime * 50, TimeUnit.MILLISECONDS) } } } diff --git a/packages/nukkit/src/console.ts b/packages/nukkit/src/console.ts index 920d5cd0..e87d15ff 100644 --- a/packages/nukkit/src/console.ts +++ b/packages/nukkit/src/console.ts @@ -1,21 +1,25 @@ import { MiaoScriptConsole } from '@ccms/api' -let Nukkit = base.getInstance().getServer(); +let Nukkit = base.getInstance().getServer() +let LogLevel = Java.type('cn.nukkit.utils.LogLevel') let CommandSender = Java.type('cn.nukkit.command.CommandSender') export class NukkitConsole extends MiaoScriptConsole { sender(sender, ...args) { if (!(sender instanceof CommandSender)) { this.error(`First parameter ${sender} not instanceof cn.nukkit.command.CommandSender can't send message!`) - return; + return } if (Object.prototype.toString.call(args[0]) === "[object Array]") { args[0].forEach(line => sender.sendMessage(this.prefix + line)) } else { - sender.sendMessage(this.prefix + args.join(' ')); + sender.sendMessage(this.prefix + args.join(' ')) } } console(...args: string[]): void { - this.sender(Nukkit.getConsoleSender(), args.join(' ')); + this.sender(Nukkit.getConsoleSender(), args.join(' ')) + } + error(...args: any[]) { + this.logger.log(LogLevel.ERROR, this.name + args.join(' ')) } } diff --git a/packages/nukkit/src/task.ts b/packages/nukkit/src/task.ts index 07c10615..981c6058 100644 --- a/packages/nukkit/src/task.ts +++ b/packages/nukkit/src/task.ts @@ -5,17 +5,14 @@ const NukkitRunnable = Java.type('cn.nukkit.scheduler.NukkitRunnable') @provideSingleton(task.TaskManager) export class NukkitTaskManager extends task.TaskManager { - @inject(plugin.PluginInstance) - private pluginInstance: any - - create0(func: Function): task.Task { - return new NukkitTask(this.pluginInstance, func) + create0(owner: plugin.Plugin, func: Function, id: number): task.Task { + return new NukkitTask(owner, func, id) } callSyncMethod(func: Function): any { return func() } disable0() { - base.getInstance().getServer().getScheduler().cancelTask(this.pluginInstance) + base.getInstance().getServer().getScheduler().cancelTask(base.getInstance()) } } @@ -24,9 +21,9 @@ export class NukkitTask extends task.Task { let run = new NukkitRunnable({ run: () => this.run(...args) }) let funcName = `runTask${this.interval ? 'Timer' : 'Later'}${this.isAsync ? 'Asynchronously' : ''}` if (this.interval) { - return run[funcName](this.plugin, this.laterTime, this.interval) + return run[funcName](base.getInstance(), this.laterTime, this.interval) } else { - return run[funcName](this.plugin, this.laterTime) + return run[funcName](base.getInstance(), this.laterTime) } } } diff --git a/packages/ployfill/src/node-shim.ts b/packages/ployfill/src/node-shim.ts index 67761407..3c4018ec 100644 --- a/packages/ployfill/src/node-shim.ts +++ b/packages/ployfill/src/node-shim.ts @@ -22,6 +22,19 @@ class Process extends EventEmitter { } } platform = Packages.java.lang.System.getProperty("os.name") + on(event: string | symbol, listener: (...args: any[]) => void) { + return super.on(event, (...args) => { + try { + listener(...args) + } catch (error) { + try { + super.emit('error', error) + } catch (error) { + console.ex(error) + } + } + }) + } nextTick(func: Function) { microTaskPool.execute(func) } diff --git a/packages/plugin/src/command.ts b/packages/plugin/src/command.ts index 6815486e..f10a4905 100644 --- a/packages/plugin/src/command.ts +++ b/packages/plugin/src/command.ts @@ -1,5 +1,5 @@ import { command, plugin, server } from '@ccms/api' -import { provideSingleton, inject, Autowired } from '@ccms/container' +import { provideSingleton, Autowired } from '@ccms/container' import { getPluginCommandMetadata, getPluginTabCompleterMetadata } from './utils' @provideSingleton(PluginCommandManager) diff --git a/packages/plugin/src/task.ts b/packages/plugin/src/task.ts index 7af21278..5d82035b 100644 --- a/packages/plugin/src/task.ts +++ b/packages/plugin/src/task.ts @@ -7,7 +7,7 @@ export class PluginTaskManager { private taskManager: task.TaskManager constructor() { - process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.cancelTask(plugin)) + process.on('plugin.after.disable', this.cancelTask.bind(this)) } private cancelTask(pluginInstance: plugin.Plugin) { diff --git a/packages/sponge/src/task.ts b/packages/sponge/src/task.ts index 5a2d1cfe..26c93dd4 100644 --- a/packages/sponge/src/task.ts +++ b/packages/sponge/src/task.ts @@ -9,24 +9,22 @@ const TimeUnit = Java.type('java.util.concurrent.TimeUnit') @provideSingleton(task.TaskManager) export class SpongeTaskManager extends task.TaskManager { - @inject(plugin.PluginInstance) - private pluginInstance: any private syncExecutor: any @postConstruct() initialize() { - this.syncExecutor = Sponge.getScheduler().createSyncExecutor(this.pluginInstance) + this.syncExecutor = Sponge.getScheduler().createSyncExecutor(base.getInstance()) } - create0(func: Function): task.Task { - return new SpongeTask(this.pluginInstance, func) + create0(owner: plugin.Plugin, func: Function, id: number): task.Task { + return new SpongeTask(owner, func, id) } callSyncMethod(func: Function): any { // @ts-ignore return this.syncExecutor.schedule(new Callable({ call: () => func() }), java.lang.Long.valueOf(0), TimeUnit.NANOSECONDS).get() } disable0() { - Sponge.getScheduler().getScheduledTasks(this.pluginInstance).forEach((task: task.Cancelable) => task.cancel()) + Sponge.getScheduler().getScheduledTasks(base.getInstance()).forEach((task: task.Cancelable) => task.cancel()) } } @@ -36,6 +34,6 @@ export class SpongeTask extends task.Task { if (this.isAsync) { run.async() } if (this.laterTime) { run.delayTicks(this.laterTime) } if (this.interval) { run.intervalTicks(this.interval) } - return run.submit(this.plugin) + return run.submit(base.getInstance()) } } diff --git a/packages/spring/src/task.ts b/packages/spring/src/task.ts index f4e67e8a..e74acc9f 100644 --- a/packages/spring/src/task.ts +++ b/packages/spring/src/task.ts @@ -2,7 +2,6 @@ import { task, plugin } from '@ccms/api' import { inject, provideSingleton } from '@ccms/container' import thread_pool from '@ccms/common/dist/thread-pool' -const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") const AtomicBoolean = Java.type("java.util.concurrent.atomic.AtomicBoolean") const Thread = Java.type('java.lang.Thread') @@ -11,21 +10,19 @@ export class SpringTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) private pluginInstance: any - private taskId: any private tasks: { [s: string]: task.Cancelable } private executor: java.util.concurrent.ThreadPoolExecutor constructor() { super() - this.taskId = new AtomicInteger(0) this.tasks = {} this.executor = thread_pool.create({ groupName: '@ccms/spring' }) } - create0(func: Function): task.Task { - return new SpringTask(this.pluginInstance, func, this) + create0(owner: plugin.Plugin, func: Function, id: number): task.Task { + return new SpringTask(owner, func, id, this) } callSyncMethod(func: Function): any { return func() @@ -51,8 +48,8 @@ export class SpringTask extends task.Task { private taskManager: SpringTaskManager private running = new AtomicBoolean(true) - constructor(plugin: any, func: Function, taskManager: SpringTaskManager) { - super(plugin, func) + constructor(owner: plugin.Plugin, func: Function, id: number, taskManager: SpringTaskManager) { + super(owner, func, id) this.id = taskManager.nextId() this.taskManager = taskManager } @@ -83,19 +80,16 @@ export class SpringTask extends task.Task { this.cancel() } - cancel0(): any { + cancel0() { var wasRunning = this.running.getAndSet(false) if (wasRunning) { this.taskManager.cancel(this.id) + return true } + return false } submit0(...args: any[]) { this.taskManager.submit(this.id, this, () => this.run(...args)) - return { - cancel: () => { - return this.cancel0() - } - } } }