Automatic action resolving with decorators and magic
This commit is contained in:
		| @@ -1,6 +1,35 @@ | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { GameState } from '../Storage/GameState'; | ||||
| import Scheduler from '../Scheduler'; | ||||
|  | ||||
| export default abstract class ActionController { | ||||
|     abstract async run(args: Args, task: Task); | ||||
| const actionMap: { [name: string]: Function | undefined } = {}; | ||||
|  | ||||
| export function registerAction(constructor: Function) { | ||||
|     console.log('REGISTER ACTION', constructor.name); | ||||
|     actionMap[constructor.name] = constructor; | ||||
| } | ||||
|  | ||||
| export function createAction( | ||||
|     name: string, | ||||
|     state: GameState, | ||||
|     scheduler: Scheduler | ||||
| ): ActionController | undefined { | ||||
|     const storedFunction = actionMap[name]; | ||||
|     if (storedFunction === undefined) { | ||||
|         return undefined; | ||||
|     } | ||||
|     const constructor = (storedFunction as unknown) as typeof ActionController; | ||||
|     return new constructor(state, scheduler); | ||||
| } | ||||
|  | ||||
| export class ActionController { | ||||
|     protected state: GameState; | ||||
|     protected scheduler: Scheduler; | ||||
|     constructor(state: GameState, scheduler: Scheduler) { | ||||
|         this.state = state; | ||||
|         this.scheduler = scheduler; | ||||
|     } | ||||
|  | ||||
|     async run(args: Args, task: Task) {} | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { BuildingQueueFullError } from '../Errors'; | ||||
|  | ||||
| export default class CheckBuildingRemainingTimeAction extends ActionController { | ||||
|     static NAME = 'check_building_remaining_time'; | ||||
|  | ||||
| @registerAction | ||||
| export class CheckBuildingRemainingTimeAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const timer = jQuery('.buildDuration .timer'); | ||||
|         if (timer.length === 1) { | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
|  | ||||
| export default class ClickButtonAction extends ActionController { | ||||
|     static NAME = 'click_button'; | ||||
| @registerAction | ||||
| export class ClickButtonAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const el = jQuery(args.selector); | ||||
|         if (el.length === 1) { | ||||
|   | ||||
| @@ -1,16 +1,9 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import Scheduler from '../Scheduler'; | ||||
|  | ||||
| export default class CompleteTaskAction extends ActionController { | ||||
|     static NAME = 'complete_task'; | ||||
|     private scheduler: Scheduler; | ||||
|  | ||||
|     constructor(scheduler: Scheduler) { | ||||
|         super(); | ||||
|         this.scheduler = scheduler; | ||||
|     } | ||||
| @registerAction | ||||
| export class CompleteTaskAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         this.scheduler.completeTask(task.id); | ||||
|     } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
|  | ||||
| export default class GoToPageAction extends ActionController { | ||||
|     static NAME = 'go_to_page'; | ||||
| @registerAction | ||||
| export class GoToPageAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         window.location.assign(args.path); | ||||
|     } | ||||
|   | ||||
| @@ -1,18 +1,10 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { ActionError } from '../Errors'; | ||||
| import { GameState } from '../Storage/GameState'; | ||||
|  | ||||
| export default class GrabHeroAttributesAction extends ActionController { | ||||
|     static NAME = 'grab_hero_attributes'; | ||||
|     private state: GameState; | ||||
|  | ||||
|     constructor(state: GameState) { | ||||
|         super(); | ||||
|         this.state = state; | ||||
|     } | ||||
|  | ||||
| @registerAction | ||||
| export class GrabHeroAttributesAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const healthElement = jQuery( | ||||
|             '#attributes .attribute.health .powervalue .value' | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { GameState } from '../Storage/GameState'; | ||||
| import { trimPrefix } from '../utils'; | ||||
| import { AbortTaskError } from '../Errors'; | ||||
|  | ||||
| @@ -13,15 +12,8 @@ interface Adventure { | ||||
|     level: number; | ||||
| } | ||||
|  | ||||
| export default class SendOnAdventureAction extends ActionController { | ||||
|     static NAME = 'send_on_adventure'; | ||||
|     private state: GameState; | ||||
|  | ||||
|     constructor(state: GameState) { | ||||
|         super(); | ||||
|         this.state = state; | ||||
|     } | ||||
|  | ||||
| @registerAction | ||||
| export class SendOnAdventureAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const adventures = this.findAdventures(); | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import ActionController from './ActionController'; | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { TryLaterError } from '../Errors'; | ||||
| import Scheduler from '../Scheduler'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
|  | ||||
| export default class UpgradeBuildingAction extends ActionController { | ||||
|     static NAME = 'upgrade_building'; | ||||
| @registerAction | ||||
| export class UpgradeBuildingAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const btn = jQuery( | ||||
|             '.upgradeButtonsContainer .section1 button.green.build' | ||||
|   | ||||
| @@ -1,25 +1,18 @@ | ||||
| import { markPage, sleepShort, timestamp } from './utils'; | ||||
| import UpgradeBuildingTask from './Task/UpgradeBuildingTask'; | ||||
| import UpgradeBuildingAction from './Action/UpgradeBuildingAction'; | ||||
| import { | ||||
|     AbortTaskError, | ||||
|     BuildingQueueFullError, | ||||
|     TryLaterError, | ||||
| } from './Errors'; | ||||
| import { TaskQueue, TaskList, Task, TaskId } from './Storage/TaskQueue'; | ||||
| import { Task, TaskId, TaskList, TaskQueue } from './Storage/TaskQueue'; | ||||
| import ActionQueue from './Storage/ActionQueue'; | ||||
| import { Args, Command } from './Common'; | ||||
| import TaskQueueRenderer from './TaskQueueRenderer'; | ||||
| import ActionController from './Action/ActionController'; | ||||
| import { ActionController, createAction } from './Action/ActionController'; | ||||
| import TaskController from './Task/TaskController'; | ||||
| import GoToPageAction from './Action/GoToPageAction'; | ||||
| import CheckBuildingRemainingTimeAction from './Action/CheckBuildingRemainingTimeAction'; | ||||
| import SendOnAdventureTask from './Task/SendOnAdventureTask'; | ||||
| import GrabHeroAttributesAction from './Action/GrabHeroAttributesAction'; | ||||
| import { GameState } from './Storage/GameState'; | ||||
| import CompleteTaskAction from './Action/CompleteTaskAction'; | ||||
| import SendOnAdventureAction from './Action/SendOnAdventureAction'; | ||||
| import ClickButtonAction from './Action/ClickButtonAction'; | ||||
|  | ||||
| export default class Scheduler { | ||||
|     private readonly version: string; | ||||
| @@ -91,7 +84,12 @@ export default class Scheduler { | ||||
|  | ||||
|     private async processTaskCommand(task: Task) { | ||||
|         const taskController = this.createTaskControllerByName(task.cmd.name); | ||||
|         this.log('PROCESS TASK', task.cmd.name, task, taskController); | ||||
|         this.log( | ||||
|             'PROCESS TASK', | ||||
|             taskController?.constructor.name, | ||||
|             task, | ||||
|             taskController | ||||
|         ); | ||||
|         if (taskController) { | ||||
|             taskController.run(task); | ||||
|         } | ||||
| @@ -99,7 +97,11 @@ export default class Scheduler { | ||||
|  | ||||
|     private async processActionCommand(cmd: Command, task: Task) { | ||||
|         const actionController = this.createActionControllerByName(cmd.name); | ||||
|         this.log('PROCESS ACTION', cmd.name, actionController); | ||||
|         this.log( | ||||
|             'PROCESS ACTION', | ||||
|             actionController?.constructor.name, | ||||
|             actionController | ||||
|         ); | ||||
|         if (actionController) { | ||||
|             await this.runAction(actionController, cmd.args, task); | ||||
|         } | ||||
| @@ -136,32 +138,15 @@ export default class Scheduler { | ||||
|     } | ||||
|  | ||||
|     private createActionControllerByName( | ||||
|         actonName: string | ||||
|         actionName: string | ||||
|     ): ActionController | undefined { | ||||
|         if (actonName === GoToPageAction.NAME) { | ||||
|             return new GoToPageAction(); | ||||
|         } | ||||
|         if (actonName === ClickButtonAction.NAME) { | ||||
|             return new ClickButtonAction(); | ||||
|         } | ||||
|         if (actonName === CompleteTaskAction.NAME) { | ||||
|             return new CompleteTaskAction(this); | ||||
|         } | ||||
|         if (actonName === UpgradeBuildingAction.NAME) { | ||||
|             return new UpgradeBuildingAction(); | ||||
|         } | ||||
|         if (actonName === CheckBuildingRemainingTimeAction.NAME) { | ||||
|             return new CheckBuildingRemainingTimeAction(); | ||||
|         } | ||||
|         if (actonName === GrabHeroAttributesAction.NAME) { | ||||
|             return new GrabHeroAttributesAction(this.gameState); | ||||
|         } | ||||
|         if (actonName === SendOnAdventureAction.NAME) { | ||||
|             return new SendOnAdventureAction(this.gameState); | ||||
|         } | ||||
|         this.logError('ACTION NOT FOUND', actonName); | ||||
|         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 { | ||||
|   | ||||
| @@ -2,11 +2,11 @@ import Scheduler from '../Scheduler'; | ||||
| import { Args, Command } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import TaskController 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'; | ||||
| 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'; | ||||
| @@ -17,25 +17,21 @@ export default class SendOnAdventureTask extends TaskController { | ||||
|         this.scheduler = scheduler; | ||||
|     } | ||||
|  | ||||
|     name(): string { | ||||
|         return SendOnAdventureTask.NAME; | ||||
|     } | ||||
|  | ||||
|     run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.NAME, { ...args, path: 'hero.php' }), | ||||
|             new Command(GrabHeroAttributesAction.NAME, args), | ||||
|             new Command(GoToPageAction.NAME, { | ||||
|             new Command(GoToPageAction.name, { ...args, path: 'hero.php' }), | ||||
|             new Command(GrabHeroAttributesAction.name, args), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: '/hero.php?t=3', | ||||
|             }), | ||||
|             new Command(SendOnAdventureAction.NAME, args), | ||||
|             new Command(ClickButtonAction.NAME, { | ||||
|             new Command(SendOnAdventureAction.name, args), | ||||
|             new Command(ClickButtonAction.name, { | ||||
|                 ...args, | ||||
|                 selector: '.adventureSendButton button', | ||||
|             }), | ||||
|             new Command(CompleteTaskAction.NAME, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
|  | ||||
| export default abstract class TaskController { | ||||
|     abstract name(): string; | ||||
|     abstract run(task: Task); | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import Scheduler from '../Scheduler'; | ||||
| import UpgradeBuildingAction from '../Action/UpgradeBuildingAction'; | ||||
| import { UpgradeBuildingAction } from '../Action/UpgradeBuildingAction'; | ||||
| import { Args, Command } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import TaskController from './TaskController'; | ||||
| import GoToPageAction from '../Action/GoToPageAction'; | ||||
| import CheckBuildingRemainingTimeAction from '../Action/CheckBuildingRemainingTimeAction'; | ||||
| import CompleteTaskAction from '../Action/CompleteTaskAction'; | ||||
| 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'; | ||||
| @@ -16,22 +16,18 @@ export default class UpgradeBuildingTask extends TaskController { | ||||
|         this.scheduler = scheduler; | ||||
|     } | ||||
|  | ||||
|     name(): string { | ||||
|         return UpgradeBuildingTask.NAME; | ||||
|     } | ||||
|  | ||||
|     run(task: Task) { | ||||
|         console.log('RUN', UpgradeBuildingTask.NAME, 'with', task); | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.NAME, { ...args, path: '/dorf1.php' }), | ||||
|             new Command(CheckBuildingRemainingTimeAction.NAME, args), | ||||
|             new Command(GoToPageAction.NAME, { | ||||
|             new Command(GoToPageAction.name, { ...args, path: '/dorf1.php' }), | ||||
|             new Command(CheckBuildingRemainingTimeAction.name, args), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: '/build.php?id=' + args.id, | ||||
|             }), | ||||
|             new Command(UpgradeBuildingAction.NAME, args), | ||||
|             new Command(CompleteTaskAction.NAME, args), | ||||
|             new Command(UpgradeBuildingAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "allowJs": true, | ||||
|     "experimentalDecorators": true, | ||||
|     "isolatedModules": true, | ||||
|     "module": "commonjs", | ||||
|     "outDir": "./dist", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user