diff --git a/src/Action/ActionController.ts b/src/Action/ActionController.ts index 4242ae6..0835a5e 100644 --- a/src/Action/ActionController.ts +++ b/src/Action/ActionController.ts @@ -1,12 +1,11 @@ import { Args } from '../Common'; import { Task } from '../Storage/TaskQueue'; import { GameState } from '../Storage/GameState'; -import Scheduler from '../Scheduler'; +import { Scheduler } from '../Scheduler'; const actionMap: { [name: string]: Function | undefined } = {}; export function registerAction(constructor: Function) { - console.log('REGISTER ACTION', constructor.name); actionMap[constructor.name] = constructor; } diff --git a/src/Dashboard.ts b/src/Dashboard.ts index 348c624..374a1d0 100644 --- a/src/Dashboard.ts +++ b/src/Dashboard.ts @@ -1,7 +1,7 @@ import * as URLParse from 'url-parse'; -import { markPage, trimPrefix, uniqId, waitForLoad } from './utils'; -import Scheduler from './Scheduler'; -import UpgradeBuildingTask from './Task/UpgradeBuildingTask'; +import { markPage, uniqId, waitForLoad } from './utils'; +import { Scheduler } from './Scheduler'; +import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; import { Command } from './Common'; import TaskQueueRenderer from './TaskQueueRenderer'; @@ -51,7 +51,7 @@ export default class Dashboard { } private onScheduleBuilding(id: string) { - const queueItem = new Command(UpgradeBuildingTask.NAME, { + const queueItem = new Command(UpgradeBuildingTask.name, { id, }); this.scheduler.scheduleTask(queueItem); diff --git a/src/Errors.ts b/src/Errors.ts index 0373ad7..16974dc 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -1,29 +1,29 @@ import { TaskId } from './Storage/TaskQueue'; export class ActionError extends Error { - readonly id: TaskId; - constructor(id: TaskId, msg: string = '') { + readonly taskId: TaskId; + constructor(taskId: TaskId, msg: string = '') { super(msg); - this.id = id; + this.taskId = taskId; Object.setPrototypeOf(this, ActionError.prototype); } } export class AbortTaskError extends Error { - readonly id: TaskId; - constructor(id: TaskId, msg: string = '') { + readonly taskId: TaskId; + constructor(taskId: TaskId, msg: string = '') { super(msg); - this.id = id; + this.taskId = taskId; Object.setPrototypeOf(this, AbortTaskError.prototype); } } export class TryLaterError extends Error { readonly seconds: number; - readonly id: TaskId; - constructor(seconds: number, id: TaskId, msg: string = '') { + readonly taskId: TaskId; + constructor(seconds: number, taskId: TaskId, msg: string = '') { super(msg); - this.id = id; + this.taskId = taskId; this.seconds = seconds; Object.setPrototypeOf(this, TryLaterError.prototype); } @@ -31,10 +31,10 @@ export class TryLaterError extends Error { export class BuildingQueueFullError extends Error { readonly seconds: number; - readonly id: TaskId; - constructor(seconds: number, id: TaskId, msg: string = '') { + readonly taskId: TaskId; + constructor(seconds: number, taskId: TaskId, msg: string = '') { super(msg); - this.id = id; + this.taskId = taskId; this.seconds = seconds; Object.setPrototypeOf(this, BuildingQueueFullError.prototype); } diff --git a/src/Scheduler.ts b/src/Scheduler.ts index edf6564..221fda9 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -1,5 +1,5 @@ import { markPage, sleepShort, timestamp } from './utils'; -import UpgradeBuildingTask from './Task/UpgradeBuildingTask'; +import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; import { AbortTaskError, BuildingQueueFullError, @@ -7,14 +7,14 @@ import { } from './Errors'; import { Task, TaskId, TaskList, TaskQueue } from './Storage/TaskQueue'; import ActionQueue from './Storage/ActionQueue'; -import { Args, Command } from './Common'; +import { Command } from './Common'; import TaskQueueRenderer from './TaskQueueRenderer'; -import { ActionController, createAction } from './Action/ActionController'; -import TaskController from './Task/TaskController'; -import SendOnAdventureTask from './Task/SendOnAdventureTask'; +import { createAction } from './Action/ActionController'; +import { createTask } from './Task/TaskController'; +import { SendOnAdventureTask } from './Task/SendOnAdventureTask'; import { GameState } from './Storage/GameState'; -export default class Scheduler { +export class Scheduler { private readonly version: string; private taskQueue: TaskQueue; private actionQueue: ActionQueue; @@ -48,9 +48,9 @@ export default class Scheduler { } private scheduleHeroAdventure() { - if (!this.taskQueue.hasNamed(SendOnAdventureTask.NAME)) { + if (!this.taskQueue.hasNamed(SendOnAdventureTask.name)) { this.taskQueue.push( - new Command(SendOnAdventureTask.NAME, {}), + new Command(SendOnAdventureTask.name, {}), timestamp() ); } @@ -62,7 +62,7 @@ export default class Scheduler { const taskCommand = this.taskQueue.get(currentTs); // текущего таска нет, очищаем очередь действий по таску - if (taskCommand === undefined) { + if (!taskCommand) { this.log('NO ACTIVE TASK'); this.actionQueue.clear(); return; @@ -73,40 +73,70 @@ export default class Scheduler { this.log('CURRENT TASK', taskCommand); this.log('CURRENT ACTION', actionCommand); - if (actionCommand) { - return await this.processActionCommand(actionCommand, taskCommand); - } + try { + if (actionCommand) { + return await this.processActionCommand( + actionCommand, + taskCommand + ); + } - if (taskCommand) { - return await this.processTaskCommand(taskCommand); - } - } - - private async processTaskCommand(task: Task) { - const taskController = this.createTaskControllerByName(task.cmd.name); - this.log( - 'PROCESS TASK', - taskController?.constructor.name, - task, - taskController - ); - if (taskController) { - taskController.run(task); + if (taskCommand) { + return await this.processTaskCommand(taskCommand); + } + } catch (e) { + this.handleError(e); } } private async processActionCommand(cmd: Command, task: Task) { - const actionController = this.createActionControllerByName(cmd.name); - this.log( - 'PROCESS ACTION', - actionController?.constructor.name, - actionController - ); + const actionController = createAction(cmd.name, this.gameState, this); + this.log('PROCESS ACTION', cmd.name, actionController); if (actionController) { - await this.runAction(actionController, cmd.args, task); + await actionController.run(cmd.args, task); + } else { + this.logError('ACTION NOT FOUND', cmd.name); } } + private async processTaskCommand(task: Task) { + const taskController = createTask(task.cmd.name, this); + this.log('PROCESS TASK', task.cmd.name, task, taskController); + if (taskController) { + await taskController.run(task); + } else { + this.logError('TASK NOT FOUND', task.cmd.name); + } + } + + private handleError(err: Error) { + this.logWarn('ACTION ABORTED', err.message); + this.actionQueue.clear(); + + if (err instanceof AbortTaskError) { + this.logWarn('ABORT TASK', err.taskId); + this.completeTask(err.taskId); + return; + } + + if (err instanceof TryLaterError) { + this.logWarn('TRY', err.taskId, 'AFTER', err.seconds); + this.taskQueue.postpone(err.taskId, timestamp() + err.seconds); + return; + } + + if (err instanceof BuildingQueueFullError) { + this.logWarn('BUILDING QUEUE FULL, TRY ALL AFTER', err.seconds); + this.taskQueue.modify( + t => t.cmd.name === UpgradeBuildingTask.name, + t => t.withTime(timestamp() + err.seconds) + ); + return; + } + + throw err; + } + getTaskItems(): TaskList { return this.taskQueue.seeItems(); } @@ -124,59 +154,15 @@ export default class Scheduler { this.actionQueue.assign(actions); } - private createTaskControllerByName( - taskName: string - ): TaskController | undefined { - switch (taskName) { - case UpgradeBuildingTask.NAME: - return new UpgradeBuildingTask(this); - case SendOnAdventureTask.NAME: - return new SendOnAdventureTask(this); - } - this.logError('TASK NOT FOUND', taskName); - return undefined; - } - - private createActionControllerByName( - actionName: string - ): ActionController | undefined { - const action = createAction(actionName, this.gameState, this); - if (!action) { - this.logError('ACTION NOT FOUND', actionName); - return undefined; - } - return action; - } - - private async runAction(action: ActionController, args: Args, task: Task) { - try { - await action.run(args, task); - } catch (e) { - console.warn('ACTION ABORTED', e.message); - this.actionQueue.clear(); - if (e instanceof AbortTaskError) { - console.warn('ABORT TASK', e.id); - this.completeTask(e.id); - } - if (e instanceof TryLaterError) { - console.warn('TRY', task.id, 'AFTER', e.seconds); - this.taskQueue.postpone(task.id, timestamp() + e.seconds); - } - if (e instanceof BuildingQueueFullError) { - console.warn('BUILDING QUEUE FULL, TRY ALL AFTER', e.seconds); - this.taskQueue.modify( - t => t.cmd.name === UpgradeBuildingTask.NAME, - t => t.withTime(timestamp() + e.seconds) - ); - } - } - } - private log(...args) { console.log('SCHEDULER:', ...args); } + private logWarn(...args) { + console.warn('SCHEDULER:', ...args); + } + private logError(...args) { - console.error(...args); + console.error('SCHEDULER:', ...args); } } diff --git a/src/Task/SendOnAdventureTask.ts b/src/Task/SendOnAdventureTask.ts index e03a49e..87e132a 100644 --- a/src/Task/SendOnAdventureTask.ts +++ b/src/Task/SendOnAdventureTask.ts @@ -1,23 +1,15 @@ -import Scheduler from '../Scheduler'; import { Args, Command } from '../Common'; import { Task } from '../Storage/TaskQueue'; -import TaskController from './TaskController'; +import { TaskController, registerTask } from './TaskController'; import { GoToPageAction } from '../Action/GoToPageAction'; import { GrabHeroAttributesAction } from '../Action/GrabHeroAttributesAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction'; import { SendOnAdventureAction } from '../Action/SendOnAdventureAction'; import { ClickButtonAction } from '../Action/ClickButtonAction'; -export default class SendOnAdventureTask extends TaskController { - static NAME = 'send_on_adventure'; - private scheduler: Scheduler; - - constructor(scheduler: Scheduler) { - super(); - this.scheduler = scheduler; - } - - run(task: Task) { +@registerTask +export class SendOnAdventureTask extends TaskController { + async run(task: Task) { const args: Args = { ...task.cmd.args, taskId: task.id }; this.scheduler.scheduleActions([ new Command(GoToPageAction.name, { ...args, path: 'hero.php' }), diff --git a/src/Task/TaskController.ts b/src/Task/TaskController.ts index 55a8dd3..17645fe 100644 --- a/src/Task/TaskController.ts +++ b/src/Task/TaskController.ts @@ -1,5 +1,30 @@ import { Task } from '../Storage/TaskQueue'; +import { Scheduler } from '../Scheduler'; -export default abstract class TaskController { - abstract run(task: Task); +const taskMap: { [name: string]: Function | undefined } = {}; + +export function registerTask(constructor: Function) { + taskMap[constructor.name] = constructor; +} + +export function createTask( + name: string, + scheduler: Scheduler +): TaskController | undefined { + const storedFunction = taskMap[name]; + if (storedFunction === undefined) { + return undefined; + } + const constructor = (storedFunction as unknown) as typeof TaskController; + return new constructor(scheduler); +} + +export class TaskController { + protected scheduler: Scheduler; + + constructor(scheduler: Scheduler) { + this.scheduler = scheduler; + } + + async run(task: Task) {} } diff --git a/src/Task/UpgradeBuildingTask.ts b/src/Task/UpgradeBuildingTask.ts index 2824753..b38e1c0 100644 --- a/src/Task/UpgradeBuildingTask.ts +++ b/src/Task/UpgradeBuildingTask.ts @@ -1,23 +1,14 @@ -import Scheduler from '../Scheduler'; import { UpgradeBuildingAction } from '../Action/UpgradeBuildingAction'; import { Args, Command } from '../Common'; import { Task } from '../Storage/TaskQueue'; -import TaskController from './TaskController'; +import { TaskController, registerTask } from './TaskController'; import { GoToPageAction } from '../Action/GoToPageAction'; import { CheckBuildingRemainingTimeAction } from '../Action/CheckBuildingRemainingTimeAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction'; -export default class UpgradeBuildingTask extends TaskController { - static NAME = 'upgrade_building'; - private scheduler: Scheduler; - - constructor(scheduler: Scheduler) { - super(); - this.scheduler = scheduler; - } - - run(task: Task) { - console.log('RUN', UpgradeBuildingTask.NAME, 'with', task); +@registerTask +export class UpgradeBuildingTask extends TaskController { + async run(task: Task) { const args: Args = { ...task.cmd.args, taskId: task.id }; this.scheduler.scheduleActions([ new Command(GoToPageAction.name, { ...args, path: '/dorf1.php' }),