diff --git a/packages/api/src/task.ts b/packages/api/src/task.ts index 3afb9d7e..471aa9f6 100644 --- a/packages/api/src/task.ts +++ b/packages/api/src/task.ts @@ -1,34 +1,62 @@ +import { plugin } from './index' +import { injectable } from '@ccms/container' + export namespace task { - export const TaskManager = Symbol('TaskManager') - export interface TaskManager { + @injectable() + export abstract class TaskManager { + protected cacheTasks = new Map() + + protected pluginCreate(plugin: plugin.Plugin, task: task.Task) { + if (!this.cacheTasks.has(plugin.description.name)) { + this.cacheTasks.set(plugin.description.name, []) + } + this.cacheTasks.get(plugin.description.name).push(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) + } + } /** * 创建任务 * @param func 任务内容 */ - create(func: Function): task.Task; + 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) + if (plugin) { return this.pluginCreate(plugin, task) } + return task + } + abstract create0(func: Function): task.Task /** * 在主线程执行代码 * @param func 执行内容 */ - callSyncMethod(func: Function): any; + abstract callSyncMethod(func: Function): any /** * 关闭任务管理器 */ - disable(); + disable(plugin?: plugin.Plugin) { + if (plugin) { return this.pluginDisable(plugin) } + this.disable0() + } + abstract disable0() } /** * 任务抽象 */ export abstract class Task { - protected plugin: any; - protected func: Function; + protected plugin: any + protected func: Function protected isAsync: boolean = false; protected laterTime: number = 0; protected interval: number = 0; constructor(plugin: any, func: Function) { - this.plugin = plugin; - this.func = func; + this.plugin = plugin + this.func = func } /** @@ -36,8 +64,8 @@ export namespace task { * @param isAsync 是否异步 */ async(isAsync: boolean = true): task.Task { - this.isAsync = isAsync; - return this; + this.isAsync = isAsync + return this } /** @@ -45,8 +73,8 @@ export namespace task { * @param tick 延时 Tick */ later(tick: number): task.Task { - this.laterTime = tick; - return this; + this.laterTime = tick + return this } /** @@ -54,16 +82,23 @@ export namespace task { * @param tick 循环时间 Tick */ timer(tick: number): task.Task { - this.interval = tick; - return this; + this.interval = tick + return this + } + + /** + * 取消任务 + */ + cancel(): boolean { + throw new Error("Method not implemented.") } protected run(...args: any[]): void { try { - this.func(...args); + this.func(...args) } catch (ex) { console.console('§4插件执行任务时发生错误', ex) - console.ex(ex); + console.ex(ex) } } @@ -71,12 +106,22 @@ export namespace task { * 提交任务 * @param args 任务参数 */ - abstract submit(...args: any[]): Cancelable; + submit(...args: any[]): Cancelable { + let cancelable = this.submit0(...args) + this.cancel = cancelable.cancel + return cancelable + } + + /** + * 提交任务 + * @param args 任务参数 + */ + abstract submit0(...args: any[]): Cancelable } /** * 返可取消的对象 */ export interface Cancelable { - cancel(): boolean; + cancel(): boolean } } diff --git a/packages/bukkit/src/task.ts b/packages/bukkit/src/task.ts index a7fa0a76..4461b9d5 100644 --- a/packages/bukkit/src/task.ts +++ b/packages/bukkit/src/task.ts @@ -1,29 +1,28 @@ import { task, plugin } from '@ccms/api' -import { inject, provideSingleton } from '@ccms/container'; +import { inject, provideSingleton } from '@ccms/container' -const Bukkit = Java.type('org.bukkit.Bukkit'); -const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable'); +const Bukkit = Java.type('org.bukkit.Bukkit') +const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable') const Callable = Java.type('java.util.concurrent.Callable') @provideSingleton(task.TaskManager) -export class BukkitTaskManager implements task.TaskManager { +export class BukkitTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) - private pluginInstance: any; + private pluginInstance: any - create(func: Function): task.Task { - if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !'); }; - return new BukkitTask(this.pluginInstance, func); + create0(func: Function): task.Task { + return new BukkitTask(this.pluginInstance, func) } callSyncMethod(func: Function): any { return Bukkit.getScheduler().callSyncMethod(this.pluginInstance, new Callable({ call: () => func() })).get() } - disable() { - Bukkit.getScheduler().cancelTasks(this.pluginInstance); + disable0() { + Bukkit.getScheduler().cancelTasks(this.pluginInstance) } } export class BukkitTask extends task.Task { - submit(...args: any[]): task.Cancelable { + submit0(...args: any[]): task.Cancelable { let run = new BukkitRunnable({ run: () => this.run(...args) }) let funcName = `runTask${this.interval ? 'Timer' : 'Later'}${this.isAsync ? 'Asynchronously' : ''}` if (this.interval) { diff --git a/packages/bungee/src/task.ts b/packages/bungee/src/task.ts index abb9b1e3..e00bf7b1 100644 --- a/packages/bungee/src/task.ts +++ b/packages/bungee/src/task.ts @@ -1,28 +1,31 @@ import { task, plugin } from '@ccms/api' -import { inject, provideSingleton } from '@ccms/container'; +import { inject, provideSingleton } from '@ccms/container' var Runnable = Java.type('java.lang.Runnable') let TimeUnit = Java.type('java.util.concurrent.TimeUnit') @provideSingleton(task.TaskManager) -export class BungeeTaskManager implements task.TaskManager { +export class BungeeTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) - private pluginInstance: any; + private pluginInstance: any - create(func: Function): task.Task { - if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !'); }; - return new BungeeTask(this.pluginInstance, func); + create(func: Function, plugin?: plugin.Plugin): task.Task { + if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !') }; + let task = new BungeeTask(this.pluginInstance, func) + if (plugin) { return this.pluginCreate(plugin, task) } + return task } callSyncMethod(func: Function): any { - return func(); + return func() } - disable() { + disable(plugin?: plugin.Plugin) { + if (plugin) { return this.pluginDisable(plugin) } this.pluginInstance.getProxy().getScheduler().cancel(this.pluginInstance) } } export class BungeeTask extends task.Task { - submit(...args: any[]): task.Cancelable { + 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) diff --git a/packages/nukkit/src/task.ts b/packages/nukkit/src/task.ts index 03216a58..07c10615 100644 --- a/packages/nukkit/src/task.ts +++ b/packages/nukkit/src/task.ts @@ -1,34 +1,32 @@ import { task, plugin } from '@ccms/api' -import { inject, provideSingleton } from '@ccms/container'; +import { inject, provideSingleton } from '@ccms/container' -const NukkitRunnable = Java.type('cn.nukkit.scheduler.NukkitRunnable'); +const NukkitRunnable = Java.type('cn.nukkit.scheduler.NukkitRunnable') @provideSingleton(task.TaskManager) -export class NukkitTaskManager implements task.TaskManager { +export class NukkitTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) - private pluginInstance: any; + private pluginInstance: any - create(func: Function): task.Task { - if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !'); }; - return new NukkitTask(this.pluginInstance, func); + create0(func: Function): task.Task { + return new NukkitTask(this.pluginInstance, func) } callSyncMethod(func: Function): any { return func() } - disable() { + disable0() { base.getInstance().getServer().getScheduler().cancelTask(this.pluginInstance) } } export class NukkitTask extends task.Task { - submit(...args: any[]): task.Cancelable { + submit0(...args: any[]): task.Cancelable { let run = new NukkitRunnable({ run: () => this.run(...args) }) let funcName = `runTask${this.interval ? 'Timer' : 'Later'}${this.isAsync ? 'Asynchronously' : ''}` if (this.interval) { - run[funcName](this.plugin, this.laterTime, this.interval); + return run[funcName](this.plugin, this.laterTime, this.interval) } else { - run[funcName](this.plugin, this.laterTime); + return run[funcName](this.plugin, this.laterTime) } - return run; } } diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 6bec8559..1829ef0a 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -4,8 +4,9 @@ import { inject, provideSingleton, Container, ContainerInstance } from '@ccms/co import './config' import { interfaces } from './interfaces' -import { PluginCommandManager } from './command' +import { PluginTaskManager } from './task' import { PluginEventManager } from './event' +import { PluginCommandManager } from './command' const Thread = Java.type('java.lang.Thread') @@ -22,10 +23,12 @@ export class PluginManagerImpl implements plugin.PluginManager { @inject(server.ServerChecker) private serverChecker: server.ServerChecker - @inject(PluginCommandManager) - private commandManager: PluginCommandManager + @inject(PluginTaskManager) + private taskManager: PluginTaskManager @inject(PluginEventManager) private eventManager: PluginEventManager + @inject(PluginCommandManager) + private commandManager: PluginCommandManager private initialized: boolean = false @@ -43,8 +46,9 @@ export class PluginManagerImpl implements plugin.PluginManager { this.metadataMap = new Map() // ignore unused - this.commandManager + this.taskManager this.eventManager + this.commandManager } initialize() { @@ -131,7 +135,7 @@ export class PluginManagerImpl implements plugin.PluginManager { if (loader.require(loadMetadata).loaded) { loadMetadata.loader = loader let metadata = loadMetadata.metadata - if (this.metadataMap.has(metadata.name)) { + if (this.metadataMap.has(metadata.name) && this.instanceMap.has(metadata.name)) { let oldMetadata = this.metadataMap.get(metadata.name) throw new Error(`Plugin ${oldMetadata.name} is already load from ${oldMetadata.source}...`) } diff --git a/packages/plugin/src/task.ts b/packages/plugin/src/task.ts new file mode 100644 index 00000000..853ce70d --- /dev/null +++ b/packages/plugin/src/task.ts @@ -0,0 +1,16 @@ +import { plugin, task } from '@ccms/api' +import { provideSingleton, inject } from '@ccms/container' + +@provideSingleton(PluginTaskManager) +export class PluginTaskManager { + @inject(task.TaskManager) + private taskManager: task.TaskManager + + constructor() { + process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.cancelTask(plugin)) + } + + private cancelTask(pluginInstance: plugin.Plugin) { + this.taskManager.disable(pluginInstance) + } +} diff --git a/packages/sponge/src/task.ts b/packages/sponge/src/task.ts index 49f45e8b..5a2d1cfe 100644 --- a/packages/sponge/src/task.ts +++ b/packages/sponge/src/task.ts @@ -8,7 +8,7 @@ const Callable = Java.type('java.util.concurrent.Callable') const TimeUnit = Java.type('java.util.concurrent.TimeUnit') @provideSingleton(task.TaskManager) -export class SpongeTaskManager implements task.TaskManager { +export class SpongeTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) private pluginInstance: any private syncExecutor: any @@ -18,21 +18,20 @@ export class SpongeTaskManager implements task.TaskManager { this.syncExecutor = Sponge.getScheduler().createSyncExecutor(this.pluginInstance) } - create(func: Function): task.Task { - if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !') } + create0(func: Function): task.Task { return new SpongeTask(this.pluginInstance, func) } callSyncMethod(func: Function): any { // @ts-ignore return this.syncExecutor.schedule(new Callable({ call: () => func() }), java.lang.Long.valueOf(0), TimeUnit.NANOSECONDS).get() } - disable() { + disable0() { Sponge.getScheduler().getScheduledTasks(this.pluginInstance).forEach((task: task.Cancelable) => task.cancel()) } } export class SpongeTask extends task.Task { - submit(...args: any[]): task.Cancelable { + submit0(...args: any[]): task.Cancelable { let run = Task.builder().execute(new Consumer({ accept: () => this.run(...args) })) if (this.isAsync) { run.async() } if (this.laterTime) { run.delayTicks(this.laterTime) } diff --git a/packages/spring/src/index.ts b/packages/spring/src/index.ts index 1e0ec712..65154e18 100644 --- a/packages/spring/src/index.ts +++ b/packages/spring/src/index.ts @@ -20,10 +20,11 @@ export default function SpringImpl(container: Container) { } const beanFactory = base.getInstance().getAutowireCapableBeanFactory() container.bind(server.Console).toConstantValue(SpringConsole) - container.bind(ioc.Autowired).toDynamicValue((ctx) => { + container.rebind(ioc.Autowired).toDynamicValue((ctx) => { var metadata: any = reduceMetadata(ctx) - if (toString.call(metadata.named) === "[object Symbol]") { return container.get(metadata.named) } - if (toString.call(metadata.named) === '[object jdk.internal.dynalink.beans.StaticClass]') { metadata.named = metadata.named.class } + let key = toString.call(metadata.named) + if (key === "[object Function]" || key === "[object Symbol]") { return container.get(metadata.named) } + if (key === '[object jdk.internal.dynalink.beans.StaticClass]') { metadata.named = metadata.named.class } return beanFactory.getBean(metadata.named) }) } diff --git a/packages/spring/src/task.ts b/packages/spring/src/task.ts index 5c2c7cf5..f4e67e8a 100644 --- a/packages/spring/src/task.ts +++ b/packages/spring/src/task.ts @@ -6,44 +6,57 @@ 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') -const taskId = new AtomicInteger(0) -const tasks: { [key: number]: task.Cancelable } = {} -const executor = thread_pool.create({ - groupName: '@ccms/spring' -}) - @provideSingleton(task.TaskManager) -export class SpringTaskManager implements task.TaskManager { +export class SpringTaskManager extends task.TaskManager { @inject(plugin.PluginInstance) private pluginInstance: any - private innerTaskId: any - private innerTasks: { [s: string]: task.Cancelable } - private innerExecutor: java.util.concurrent.ThreadPoolExecutor + private taskId: any + private tasks: { [s: string]: task.Cancelable } + private executor: java.util.concurrent.ThreadPoolExecutor constructor() { - this.innerTaskId = taskId - this.innerTasks = tasks - this.innerExecutor = executor + super() + this.taskId = new AtomicInteger(0) + this.tasks = {} + this.executor = thread_pool.create({ + groupName: '@ccms/spring' + }) } - create(func: Function): task.Task { - if (Object.prototype.toString.call(func) !== "[object Function]") { throw TypeError('第一个参数 Task 必须为 function !') } - return new SpringTask(this.pluginInstance, func) + create0(func: Function): task.Task { + return new SpringTask(this.pluginInstance, func, this) } callSyncMethod(func: Function): any { return func() } - disable() { - Object.values(this.innerTasks).forEach((task) => task?.cancel()) - this.innerExecutor.shutdown() + disable0() { + Object.values(this.tasks).forEach((task) => task?.cancel()) + this.executor.shutdown() + } + nextId() { + return this.taskId.incrementAndGet() + } + submit(id: number, task: SpringTask, func: Function) { + this.tasks[id] = task + this.executor.execute(func as any) + } + cancel(id: number) { + delete this.tasks[id] } } export class SpringTask extends task.Task { - public id = taskId.incrementAndGet() + private id: number + private taskManager: SpringTaskManager private running = new AtomicBoolean(true) + constructor(plugin: any, func: Function, taskManager: SpringTaskManager) { + super(plugin, func) + this.id = taskManager.nextId() + this.taskManager = taskManager + } + run(...args: any[]) { if (this.laterTime > 0) { try { @@ -70,19 +83,18 @@ export class SpringTask extends task.Task { this.cancel() } - cancel(): any { + cancel0(): any { var wasRunning = this.running.getAndSet(false) if (wasRunning) { - delete tasks[this.id] + this.taskManager.cancel(this.id) } } - submit(...args: any[]): task.Cancelable { - tasks[this.id] = this - executor.execute((() => this.run(...args)) as any) + submit0(...args: any[]) { + this.taskManager.submit(this.id, this, () => this.run(...args)) return { cancel: () => { - return this.cancel() + return this.cancel0() } } }