Extract scheduler to separate component
This commit is contained in:
parent
c285338705
commit
631151ab0b
@ -1,30 +1,30 @@
|
|||||||
import * as URLParse from 'url-parse';
|
import * as URLParse from 'url-parse';
|
||||||
import { getNumber, uniqId, waitForLoad } from '../utils';
|
import { getNumber, uniqId, waitForLoad } from './utils';
|
||||||
import { Scheduler } from '../Scheduler';
|
import { Scheduler } from './Scheduler';
|
||||||
import { BuildPage } from '../Page/BuildPage';
|
import { BuildPage } from './Page/BuildPage';
|
||||||
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock';
|
import { grabActiveVillageId, grabVillageList } from './Page/VillageBlock';
|
||||||
import {
|
import {
|
||||||
grabResourceDeposits,
|
grabResourceDeposits,
|
||||||
onResourceSlotCtrlClick,
|
onResourceSlotCtrlClick,
|
||||||
showBuildingSlotIds,
|
showBuildingSlotIds,
|
||||||
showResourceSlotIds,
|
showResourceSlotIds,
|
||||||
} from '../Page/SlotBlock';
|
} from './Page/SlotBlock';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import DashboardApp from './Components/DashboardApp.vue';
|
import DashboardApp from './DashboardView/Dashboard.vue';
|
||||||
import { ResourcesToLevel } from '../Task/ResourcesToLevel';
|
import { ResourcesToLevel } from './Task/ResourcesToLevel';
|
||||||
import { ConsoleLogger, Logger } from '../Logger';
|
import { ConsoleLogger, Logger } from './Logger';
|
||||||
import { VillageState } from '../State/VillageState';
|
import { VillageState } from './State/VillageState';
|
||||||
import { StateGrabberManager } from '../State/StateGrabberManager';
|
import { StateGrabberManager } from './State/StateGrabberManager';
|
||||||
|
|
||||||
interface QuickAction {
|
interface QuickAction {
|
||||||
label: string;
|
label: string;
|
||||||
cb: () => void;
|
cb: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dashboard {
|
export class ControlPanel {
|
||||||
private readonly version: string;
|
private readonly version: string;
|
||||||
private scheduler: Scheduler;
|
private readonly scheduler: Scheduler;
|
||||||
private grabbers: StateGrabberManager;
|
private grabbers: StateGrabberManager;
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
|
|
@ -71,7 +71,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { path } from '../../utils';
|
import { path } from '../utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
134
src/Executor.ts
Normal file
134
src/Executor.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { markPage, sleepMicro, timestamp, waitForLoad } from './utils';
|
||||||
|
import { AbortTaskError, ActionError, BuildingQueueFullError, TryLaterError } from './Errors';
|
||||||
|
import { Task } from './Storage/TaskQueue';
|
||||||
|
import { Command } from './Common';
|
||||||
|
import { TaskQueueRenderer } from './TaskQueueRenderer';
|
||||||
|
import { createAction } from './Action/ActionController';
|
||||||
|
import { createTask } from './Task/TaskController';
|
||||||
|
import { ConsoleLogger, Logger } from './Logger';
|
||||||
|
import { StateGrabberManager } from './State/StateGrabberManager';
|
||||||
|
import { Scheduler } from './Scheduler';
|
||||||
|
|
||||||
|
export class Executor {
|
||||||
|
private readonly version: string;
|
||||||
|
private readonly scheduler: Scheduler;
|
||||||
|
private grabbers: StateGrabberManager;
|
||||||
|
private logger: Logger;
|
||||||
|
|
||||||
|
constructor(version: string, scheduler: Scheduler) {
|
||||||
|
this.version = version;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.grabbers = new StateGrabberManager();
|
||||||
|
this.logger = new ConsoleLogger(this.constructor.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
await waitForLoad();
|
||||||
|
await sleepMicro();
|
||||||
|
markPage('Executor', this.version);
|
||||||
|
|
||||||
|
this.renderTaskQueue();
|
||||||
|
setInterval(() => this.renderTaskQueue(), 5 * 1000);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
await this.doTaskProcessingStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTaskQueue() {
|
||||||
|
this.logger.log('RENDER TASK QUEUE');
|
||||||
|
new TaskQueueRenderer().render(this.scheduler.getTaskItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async doTaskProcessingStep() {
|
||||||
|
await sleepMicro();
|
||||||
|
const currentTs = timestamp();
|
||||||
|
const taskCommand = this.scheduler.nextTask(currentTs);
|
||||||
|
|
||||||
|
// текущего таска нет, очищаем очередь действий по таску
|
||||||
|
if (!taskCommand) {
|
||||||
|
this.logger.log('NO ACTIVE TASK');
|
||||||
|
this.scheduler.clearActions();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionCommand = this.scheduler.nextAction();
|
||||||
|
|
||||||
|
this.logger.log('CURRENT TASK', taskCommand);
|
||||||
|
this.logger.log('CURRENT ACTION', actionCommand);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (actionCommand) {
|
||||||
|
return await this.processActionCommand(actionCommand, taskCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taskCommand) {
|
||||||
|
return await this.processTaskCommand(taskCommand);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.handleError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processActionCommand(cmd: Command, task: Task) {
|
||||||
|
this.runGrabbers();
|
||||||
|
const actionController = createAction(cmd.name, this.scheduler);
|
||||||
|
this.logger.log('PROCESS ACTION', cmd.name, actionController);
|
||||||
|
if (actionController) {
|
||||||
|
await actionController.run(cmd.args, task);
|
||||||
|
} else {
|
||||||
|
this.logger.warn('ACTION NOT FOUND', cmd.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processTaskCommand(task: Task) {
|
||||||
|
const taskController = createTask(task.name, this.scheduler);
|
||||||
|
this.logger.log('PROCESS TASK', task.name, task, taskController);
|
||||||
|
if (taskController) {
|
||||||
|
await taskController.run(task);
|
||||||
|
} else {
|
||||||
|
this.logger.warn('TASK NOT FOUND', task.name);
|
||||||
|
this.scheduler.completeTask(task.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(err: Error) {
|
||||||
|
this.scheduler.clearActions();
|
||||||
|
|
||||||
|
if (err instanceof AbortTaskError) {
|
||||||
|
this.logger.warn('ABORT TASK', err.taskId);
|
||||||
|
this.scheduler.completeTask(err.taskId);
|
||||||
|
this.scheduler.clearActions();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof TryLaterError) {
|
||||||
|
this.logger.warn('TRY', err.taskId, 'AFTER', err.seconds);
|
||||||
|
this.scheduler.postponeTask(err.taskId, err.seconds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof BuildingQueueFullError) {
|
||||||
|
this.logger.warn('BUILDING QUEUE FULL, TRY ALL AFTER', err.seconds);
|
||||||
|
this.scheduler.postponeBuildingsInVillage(err.villageId, err.seconds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof ActionError) {
|
||||||
|
this.logger.warn('ACTION ABORTED', err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.error(err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
private runGrabbers() {
|
||||||
|
try {
|
||||||
|
this.logger.log('Rug grabbers');
|
||||||
|
this.grabbers.grab();
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.warn('Grabbers fails with', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
src/Scheduler.ts
166
src/Scheduler.ts
@ -1,54 +1,27 @@
|
|||||||
import { markPage, sleepMicro, timestamp, waitForLoad } from './utils';
|
import { timestamp } from './utils';
|
||||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
import { AbortTaskError, ActionError, BuildingQueueFullError, TryLaterError } from './Errors';
|
import { TaskId, TaskList, TaskQueue } from './Storage/TaskQueue';
|
||||||
import { Task, TaskId, TaskList, TaskQueue } from './Storage/TaskQueue';
|
|
||||||
import { ActionQueue } from './Storage/ActionQueue';
|
|
||||||
import { Args, Command } from './Common';
|
import { Args, Command } from './Common';
|
||||||
import { TaskQueueRenderer } from './TaskQueueRenderer';
|
|
||||||
import { createAction } from './Action/ActionController';
|
|
||||||
import { createTask } from './Task/TaskController';
|
|
||||||
import { SendOnAdventureTask } from './Task/SendOnAdventureTask';
|
import { SendOnAdventureTask } from './Task/SendOnAdventureTask';
|
||||||
import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask';
|
import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask';
|
||||||
import { ConsoleLogger, Logger } from './Logger';
|
import { ConsoleLogger, Logger } from './Logger';
|
||||||
import { BuildBuildingTask } from './Task/BuildBuildingTask';
|
import { BuildBuildingTask } from './Task/BuildBuildingTask';
|
||||||
import { GrabVillageState } from './Task/GrabVillageState';
|
import { GrabVillageState } from './Task/GrabVillageState';
|
||||||
import { StateGrabberManager } from './State/StateGrabberManager';
|
import { ActionQueue } from './Storage/ActionQueue';
|
||||||
|
|
||||||
export class Scheduler {
|
export class Scheduler {
|
||||||
private readonly version: string;
|
|
||||||
private taskQueue: TaskQueue;
|
private taskQueue: TaskQueue;
|
||||||
private actionQueue: ActionQueue;
|
private actionQueue: ActionQueue;
|
||||||
private grabbers: StateGrabberManager;
|
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(version: string) {
|
constructor() {
|
||||||
this.version = version;
|
|
||||||
this.taskQueue = new TaskQueue();
|
this.taskQueue = new TaskQueue();
|
||||||
this.actionQueue = new ActionQueue();
|
this.actionQueue = new ActionQueue();
|
||||||
this.grabbers = new StateGrabberManager();
|
|
||||||
this.logger = new ConsoleLogger(this.constructor.name);
|
this.logger = new ConsoleLogger(this.constructor.name);
|
||||||
}
|
|
||||||
|
|
||||||
async run() {
|
|
||||||
await waitForLoad();
|
|
||||||
await sleepMicro();
|
|
||||||
markPage('Executor', this.version);
|
|
||||||
|
|
||||||
this.renderTaskQueue();
|
|
||||||
setInterval(() => this.renderTaskQueue(), 5 * 1000);
|
|
||||||
|
|
||||||
this.scheduleUniqTask(3600, SendOnAdventureTask.name);
|
this.scheduleUniqTask(3600, SendOnAdventureTask.name);
|
||||||
this.scheduleUniqTask(1200, BalanceHeroResourcesTask.name);
|
this.scheduleUniqTask(1200, BalanceHeroResourcesTask.name);
|
||||||
this.scheduleUniqTask(180, GrabVillageState.name);
|
this.scheduleUniqTask(180, GrabVillageState.name);
|
||||||
|
|
||||||
while (true) {
|
|
||||||
await this.doTaskProcessingStep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderTaskQueue() {
|
|
||||||
this.logger.log('RENDER TASK QUEUE');
|
|
||||||
new TaskQueueRenderer().render(this.taskQueue.seeItems());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private scheduleUniqTask(seconds: number, name: string, args: Args = {}) {
|
private scheduleUniqTask(seconds: number, name: string, args: Args = {}) {
|
||||||
@ -61,111 +34,16 @@ export class Scheduler {
|
|||||||
setInterval(taskScheduler, seconds * 1000);
|
setInterval(taskScheduler, seconds * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doTaskProcessingStep() {
|
|
||||||
await sleepMicro();
|
|
||||||
const currentTs = timestamp();
|
|
||||||
const taskCommand = this.taskQueue.get(currentTs);
|
|
||||||
|
|
||||||
// текущего таска нет, очищаем очередь действий по таску
|
|
||||||
if (!taskCommand) {
|
|
||||||
this.logger.log('NO ACTIVE TASK');
|
|
||||||
this.actionQueue.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionCommand = this.actionQueue.pop();
|
|
||||||
|
|
||||||
this.logger.log('CURRENT TASK', taskCommand);
|
|
||||||
this.logger.log('CURRENT ACTION', actionCommand);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (actionCommand) {
|
|
||||||
return await this.processActionCommand(actionCommand, taskCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (taskCommand) {
|
|
||||||
return await this.processTaskCommand(taskCommand);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.handleError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async processActionCommand(cmd: Command, task: Task) {
|
|
||||||
this.runGrabbers();
|
|
||||||
const actionController = createAction(cmd.name, this);
|
|
||||||
this.logger.log('PROCESS ACTION', cmd.name, actionController);
|
|
||||||
if (actionController) {
|
|
||||||
await actionController.run(cmd.args, task);
|
|
||||||
} else {
|
|
||||||
this.logger.warn('ACTION NOT FOUND', cmd.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async processTaskCommand(task: Task) {
|
|
||||||
const taskController = createTask(task.name, this);
|
|
||||||
this.logger.log('PROCESS TASK', task.name, task, taskController);
|
|
||||||
if (taskController) {
|
|
||||||
await taskController.run(task);
|
|
||||||
} else {
|
|
||||||
this.logger.warn('TASK NOT FOUND', task.name);
|
|
||||||
this.taskQueue.complete(task.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleError(err: Error) {
|
|
||||||
this.actionQueue.clear();
|
|
||||||
|
|
||||||
if (err instanceof AbortTaskError) {
|
|
||||||
this.logger.warn('ABORT TASK', err.taskId);
|
|
||||||
this.completeTask(err.taskId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof TryLaterError) {
|
|
||||||
this.logger.warn('TRY', err.taskId, 'AFTER', err.seconds);
|
|
||||||
this.taskQueue.postpone(err.taskId, timestamp() + err.seconds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof BuildingQueueFullError) {
|
|
||||||
this.logger.warn('BUILDING QUEUE FULL, TRY ALL AFTER', err.seconds);
|
|
||||||
this.taskQueue.modify(
|
|
||||||
t => t.name === BuildBuildingTask.name && t.args.villageId === err.villageId,
|
|
||||||
t => t.withTime(timestamp() + err.seconds)
|
|
||||||
);
|
|
||||||
this.taskQueue.modify(
|
|
||||||
t => t.name === UpgradeBuildingTask.name && t.args.villageId === err.villageId,
|
|
||||||
t => t.withTime(timestamp() + err.seconds)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof ActionError) {
|
|
||||||
this.logger.warn('ACTION ABORTED', err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.error(err.message);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
private runGrabbers() {
|
|
||||||
try {
|
|
||||||
this.logger.log('Rug grabbers');
|
|
||||||
this.grabbers.grab();
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.warn('Grabbers fails with', e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getTaskItems(): TaskList {
|
getTaskItems(): TaskList {
|
||||||
return this.taskQueue.seeItems();
|
return this.taskQueue.seeItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
completeTask(id: TaskId) {
|
nextTask(ts: number) {
|
||||||
this.taskQueue.complete(id);
|
return this.taskQueue.get(ts);
|
||||||
this.actionQueue.clear();
|
}
|
||||||
|
|
||||||
|
nextAction() {
|
||||||
|
return this.actionQueue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleTask(name: string, args: Args): void {
|
scheduleTask(name: string, args: Args): void {
|
||||||
@ -173,12 +51,36 @@ export class Scheduler {
|
|||||||
this.taskQueue.push(name, args, timestamp());
|
this.taskQueue.push(name, args, timestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completeTask(id: TaskId) {
|
||||||
|
this.taskQueue.complete(id);
|
||||||
|
this.actionQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
removeTask(id: TaskId) {
|
removeTask(id: TaskId) {
|
||||||
this.taskQueue.remove(id);
|
this.taskQueue.remove(id);
|
||||||
this.actionQueue.clear();
|
this.actionQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postponeTask(id: TaskId, deltaTs: number) {
|
||||||
|
this.taskQueue.postpone(id, timestamp() + deltaTs);
|
||||||
|
}
|
||||||
|
|
||||||
|
postponeBuildingsInVillage(villageId: number, seconds: number) {
|
||||||
|
this.taskQueue.modify(
|
||||||
|
t => t.name === BuildBuildingTask.name && t.args.villageId === villageId,
|
||||||
|
t => t.withTime(timestamp() + seconds)
|
||||||
|
);
|
||||||
|
this.taskQueue.modify(
|
||||||
|
t => t.name === UpgradeBuildingTask.name && t.args.villageId === villageId,
|
||||||
|
t => t.withTime(timestamp() + seconds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
scheduleActions(actions: Array<Command>): void {
|
scheduleActions(actions: Array<Command>): void {
|
||||||
this.actionQueue.assign(actions);
|
this.actionQueue.assign(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearActions() {
|
||||||
|
this.actionQueue.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ActionQueue {
|
export class ActionQueue {
|
||||||
private readonly logger;
|
private readonly logger: Logger;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logger = new ConsoleLogger(this.constructor.name);
|
this.logger = new ConsoleLogger(this.constructor.name);
|
||||||
|
28
src/index.js
28
src/index.js
@ -1,18 +1,24 @@
|
|||||||
|
import { ConsoleLogger } from './Logger';
|
||||||
import { ModeDetector } from './ModeDetector';
|
import { ModeDetector } from './ModeDetector';
|
||||||
import { Scheduler } from './Scheduler';
|
import { Scheduler } from './Scheduler';
|
||||||
import { Dashboard } from './Dashboard/Dashboard';
|
import { Executor } from './Executor';
|
||||||
|
import { ControlPanel } from './ControlPanel';
|
||||||
import TxtVersion from '!!raw-loader!./version.txt';
|
import TxtVersion from '!!raw-loader!./version.txt';
|
||||||
|
|
||||||
console.log('TRAVIAN AUTOMATION', TxtVersion);
|
const logger = new ConsoleLogger('Travian');
|
||||||
|
|
||||||
const md = new ModeDetector();
|
logger.log('TRAVIAN AUTOMATION', TxtVersion);
|
||||||
if (md.isAuto()) {
|
|
||||||
md.setAuto();
|
const modeDetector = new ModeDetector();
|
||||||
console.log('AUTO MANAGEMENT ON');
|
const scheduler = new Scheduler();
|
||||||
const scheduler = new Scheduler(TxtVersion);
|
|
||||||
scheduler.run();
|
if (modeDetector.isAuto()) {
|
||||||
|
modeDetector.setAuto();
|
||||||
|
logger.log('AUTO MANAGEMENT ON');
|
||||||
|
const executor = new Executor(TxtVersion, scheduler);
|
||||||
|
executor.run();
|
||||||
} else {
|
} else {
|
||||||
console.log('NORMAL MODE');
|
logger.log('NORMAL MODE');
|
||||||
const dashboard = new Dashboard(TxtVersion, new Scheduler());
|
const controlPanel = new ControlPanel(TxtVersion, scheduler);
|
||||||
dashboard.run();
|
controlPanel.run();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user