Refactoring
This commit is contained in:
parent
29485d233d
commit
f45f9c88ae
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
142
src/Scheduler.ts
142
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);
|
||||
|
||||
try {
|
||||
if (actionCommand) {
|
||||
return await this.processActionCommand(actionCommand, taskCommand);
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -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' }),
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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' }),
|
||||
|
Loading…
Reference in New Issue
Block a user