Add multiple village support
This commit is contained in:
		| @@ -1,9 +1,10 @@ | ||||
| import * as URLParse from 'url-parse'; | ||||
| import { elClassId, markPage, waitForLoad } from '../utils'; | ||||
| import { markPage, waitForLoad } from '../utils'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { TaskQueueRenderer } from '../TaskQueueRenderer'; | ||||
| import { BuildPage } from './BuildPage'; | ||||
| import { BuildPage } from '../Page/BuildPage'; | ||||
| import { grabActiveVillageId, showBuildingSlotIds, showFieldsSlotIds } from '../Page/EveryPage'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
|  | ||||
| export class Dashboard { | ||||
|     private readonly version: string; | ||||
| @@ -24,12 +25,19 @@ export class Dashboard { | ||||
|         this.renderTaskQueue(); | ||||
|         setInterval(() => this.renderTaskQueue(), 5000); | ||||
|  | ||||
|         const villageId = grabActiveVillageId(); | ||||
|  | ||||
|         const tasks = this.scheduler.getTaskItems(); | ||||
|         const buildingsInQueue = tasks | ||||
|             .filter(t => t.name === UpgradeBuildingTask.name && t.args.villageId === villageId) | ||||
|             .map(t => t.args.buildId); | ||||
|  | ||||
|         if (p.pathname === '/dorf1.php') { | ||||
|             this.showSlotIds('buildingSlot'); | ||||
|             showFieldsSlotIds(buildingsInQueue); | ||||
|         } | ||||
|  | ||||
|         if (p.pathname === '/dorf2.php') { | ||||
|             this.showSlotIds('aid'); | ||||
|             showBuildingSlotIds(buildingsInQueue); | ||||
|         } | ||||
|  | ||||
|         if (p.pathname === '/build.php') { | ||||
| @@ -42,27 +50,6 @@ export class Dashboard { | ||||
|         new TaskQueueRenderer().render(this.scheduler.getTaskItems()); | ||||
|     } | ||||
|  | ||||
|     private showSlotIds(prefix: string) { | ||||
|         const tasks = this.scheduler.getTaskItems(); | ||||
|         jQuery('.level.colorLayer').each((idx, el) => { | ||||
|             const buildId = elClassId(jQuery(el).attr('class') || '', prefix); | ||||
|             const oldLabel = jQuery(el) | ||||
|                 .find('.labelLayer') | ||||
|                 .text(); | ||||
|             jQuery(el) | ||||
|                 .find('.labelLayer') | ||||
|                 .text(buildId + ':' + oldLabel); | ||||
|             const inQueue = tasks.find( | ||||
|                 t => t.cmd.name === UpgradeBuildingTask.name && Number(t.cmd.args.id) === Number(buildId) | ||||
|             ); | ||||
|             if (inQueue !== undefined) { | ||||
|                 jQuery(el).css({ | ||||
|                     'background-image': 'linear-gradient(to top, #f00, #f00 100%)', | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private log(...args) { | ||||
|         console.log('SCHEDULER:', ...args); | ||||
|     } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { elClassId, split, uniqId } from '../utils'; | ||||
| import { Command } from '../Common'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { TrainTroopTask } from '../Task/TrainTroopTask'; | ||||
| import { grabActiveVillageId } from './EveryPage'; | ||||
| 
 | ||||
| const QUARTERS_ID = 19; | ||||
| 
 | ||||
| @@ -34,12 +34,10 @@ export class BuildPage { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private onScheduleBuilding(id: number) { | ||||
|         const queueItem = new Command(UpgradeBuildingTask.name, { | ||||
|             id, | ||||
|         }); | ||||
|         this.scheduler.scheduleTask(queueItem); | ||||
|         const n = new Notification(`Building ${id} scheduled`); | ||||
|     private onScheduleBuilding(buildId: number) { | ||||
|         const villageId = grabActiveVillageId(); | ||||
|         this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); | ||||
|         const n = new Notification(`Building ${buildId} scheduled`); | ||||
|         setTimeout(() => n && n.close(), 4000); | ||||
|     } | ||||
| 
 | ||||
| @@ -63,18 +61,18 @@ export class BuildPage { | ||||
| 
 | ||||
|     private onTrainTroopClick(buildId: Number, troopId: Number, el: HTMLElement) { | ||||
|         console.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId); | ||||
|         const villageId = grabActiveVillageId(); | ||||
|         const input = jQuery(el).find(`input[name="t${troopId}"]`); | ||||
|         const count = Number(input.val()); | ||||
|         if (!isNaN(count) && count > 0) { | ||||
|             console.log('PREPARE TO TRAIN', count, 'TROOPERS'); | ||||
|             for (let n of split(count)) { | ||||
|                 this.scheduler.scheduleTask( | ||||
|                     new Command(TrainTroopTask.name, { | ||||
|                 this.scheduler.scheduleTask(TrainTroopTask.name, { | ||||
|                     villageId, | ||||
|                     buildId, | ||||
|                     troopId, | ||||
|                     trainCount: n, | ||||
|                     }) | ||||
|                 ); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
							
								
								
									
										35
									
								
								src/Page/EveryPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/Page/EveryPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import * as URLParse from 'url-parse'; | ||||
| import { elClassId, getNumber } from '../utils'; | ||||
|  | ||||
| export function grabActiveVillageId(): number { | ||||
|     const href = jQuery('#sidebarBoxVillagelist a.active').attr('href'); | ||||
|     const p = new URLParse(href || '', true); | ||||
|     console.log('VILLAGE REF', href, p); | ||||
|     return getNumber(p.query.newdid); | ||||
| } | ||||
|  | ||||
| function showSlotIds(prefix: string, buildingIds: number[]): void { | ||||
|     jQuery('.level.colorLayer').each((idx, el) => { | ||||
|         const buildId = getNumber(elClassId(jQuery(el).attr('class') || '', prefix)); | ||||
|         const oldLabel = jQuery(el) | ||||
|             .find('.labelLayer') | ||||
|             .text(); | ||||
|         jQuery(el) | ||||
|             .find('.labelLayer') | ||||
|             .text(buildId + ':' + oldLabel); | ||||
|         const inQueue = buildingIds.includes(buildId); | ||||
|         if (inQueue) { | ||||
|             jQuery(el).css({ | ||||
|                 'background-image': 'linear-gradient(to top, #f00, #f00 100%)', | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function showFieldsSlotIds(buildingIds: number[]) { | ||||
|     showSlotIds('buildingSlot', buildingIds); | ||||
| } | ||||
|  | ||||
| export function showBuildingSlotIds(buildingIds: number[]) { | ||||
|     showSlotIds('aid', buildingIds); | ||||
| } | ||||
| @@ -47,7 +47,7 @@ export class Scheduler { | ||||
|     private scheduleUniqTask(seconds: number, name: string, args: Args = {}) { | ||||
|         const taskScheduler = () => { | ||||
|             if (!this.taskQueue.hasNamed(name)) { | ||||
|                 this.taskQueue.push(new Command(name, args), timestamp() + 10 * 60); | ||||
|                 this.taskQueue.push(name, args, timestamp() + 10 * 60); | ||||
|             } | ||||
|         }; | ||||
|         taskScheduler(); | ||||
| @@ -95,12 +95,12 @@ export class Scheduler { | ||||
|     } | ||||
|  | ||||
|     private async processTaskCommand(task: Task) { | ||||
|         const taskController = createTask(task.cmd.name, this); | ||||
|         this.log('PROCESS TASK', task.cmd.name, task, taskController); | ||||
|         const taskController = createTask(task.name, this); | ||||
|         this.log('PROCESS TASK', task.name, task, taskController); | ||||
|         if (taskController) { | ||||
|             await taskController.run(task); | ||||
|         } else { | ||||
|             this.logWarn('TASK NOT FOUND', task.cmd.name); | ||||
|             this.logWarn('TASK NOT FOUND', task.name); | ||||
|             this.taskQueue.complete(task.id); | ||||
|         } | ||||
|     } | ||||
| @@ -123,7 +123,7 @@ export class Scheduler { | ||||
|         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.name === UpgradeBuildingTask.name, | ||||
|                 t => t.withTime(timestamp() + err.seconds) | ||||
|             ); | ||||
|             return; | ||||
| @@ -146,9 +146,9 @@ export class Scheduler { | ||||
|         this.taskQueue.complete(id); | ||||
|     } | ||||
|  | ||||
|     scheduleTask(task: Command): void { | ||||
|         this.log('PUSH TASK', task); | ||||
|         this.taskQueue.push(task, timestamp()); | ||||
|     scheduleTask(name: string, args: Args): void { | ||||
|         this.log('PUSH TASK', name, args); | ||||
|         this.taskQueue.push(name, args, timestamp()); | ||||
|     } | ||||
|  | ||||
|     scheduleActions(actions: Array<Command>): void { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { Command } from '../Common'; | ||||
| import { Args } from '../Common'; | ||||
| import { uniqId } from '../utils'; | ||||
|  | ||||
| const QUEUE_NAME = 'task_queue:v3'; | ||||
| const QUEUE_NAME = 'task_queue:v4'; | ||||
|  | ||||
| export type TaskId = string; | ||||
|  | ||||
| @@ -12,25 +12,27 @@ function uniqTaskId(): TaskId { | ||||
| export class Task { | ||||
|     readonly id: TaskId; | ||||
|     readonly ts: number; | ||||
|     readonly cmd: Command; | ||||
|     constructor(id: TaskId, ts: number, cmd: Command) { | ||||
|     readonly name: string; | ||||
|     readonly args: Args; | ||||
|     constructor(id: TaskId, ts: number, name: string, args: Args) { | ||||
|         this.id = id; | ||||
|         this.ts = ts; | ||||
|         this.cmd = cmd; | ||||
|         this.name = name; | ||||
|         this.args = args; | ||||
|     } | ||||
|  | ||||
|     withTime(ts: number): Task { | ||||
|         return new Task(this.id, ts, this.cmd); | ||||
|         return new Task(this.id, ts, this.name, this.args); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export type TaskList = Array<Task>; | ||||
|  | ||||
| export class TaskQueue { | ||||
|     push(cmd: Command, ts: number): Task { | ||||
|     push(name: string, args: Args, ts: number): Task { | ||||
|         const id = uniqTaskId(); | ||||
|         const task = new Task(id, ts, cmd); | ||||
|         this.log('PUSH TASK', id, ts, cmd); | ||||
|         const task = new Task(id, ts, name, args); | ||||
|         this.log('PUSH TASK', id, ts, name, args); | ||||
|         let items = this.getItems(); | ||||
|         items.push(task); | ||||
|         this.flushItems(items); | ||||
| @@ -51,7 +53,7 @@ export class TaskQueue { | ||||
|     } | ||||
|  | ||||
|     hasNamed(name: string): boolean { | ||||
|         return this.has(t => t.cmd.name === name); | ||||
|         return this.has(t => t.name === name); | ||||
|     } | ||||
|  | ||||
|     modify(predicate: (t: Task) => boolean, modifier: (t: Task) => Task) { | ||||
| @@ -101,7 +103,7 @@ export class TaskQueue { | ||||
|         const storedItems = serialized !== null ? (JSON.parse(serialized) as Array<{ [key: string]: any }>) : []; | ||||
|         const items: TaskList = []; | ||||
|         storedItems.forEach(obj => { | ||||
|             items.push(new Task(obj.id || uniqId(), +obj.ts, obj.cmd)); | ||||
|             items.push(new Task(obj.id || uniqId(), +obj.ts, obj.name, obj.args)); | ||||
|         }); | ||||
|         return items; | ||||
|     } | ||||
|   | ||||
| @@ -3,18 +3,24 @@ import { Task } from '../Storage/TaskQueue'; | ||||
| import { TaskController, registerTask } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { TrainTrooperAction } from '../Action/TrainTrooperAction'; | ||||
| import { GrabVillageResourcesAction } from '../Action/GrabVillageResourcesAction'; | ||||
| import { BalanceHeroResourcesAction } from '../Action/BalanceHeroResourcesAction'; | ||||
| import { path } from '../utils'; | ||||
|  | ||||
| @registerTask | ||||
| export class BalanceHeroResourcesTask extends TaskController { | ||||
|     async run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         const args: Args = { ...task.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.name, { ...args, path: '/dorf1.php' }), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/dorf1.php'), | ||||
|             }), | ||||
|             new Command(GrabVillageResourcesAction.name, args), | ||||
|             new Command(GoToPageAction.name, { ...args, path: 'hero.php' }), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/hero.php'), | ||||
|             }), | ||||
|             new Command(BalanceHeroResourcesAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|   | ||||
| @@ -6,17 +6,21 @@ import { GrabHeroAttributesAction } from '../Action/GrabHeroAttributesAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { SendOnAdventureAction } from '../Action/SendOnAdventureAction'; | ||||
| import { ClickButtonAction } from '../Action/ClickButtonAction'; | ||||
| import { path } from '../utils'; | ||||
|  | ||||
| @registerTask | ||||
| export class SendOnAdventureTask extends TaskController { | ||||
|     async run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         const args: Args = { ...task.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.name, { ...args, path: 'hero.php' }), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/hero.php'), | ||||
|             }), | ||||
|             new Command(GrabHeroAttributesAction.name, args), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: '/hero.php?t=3', | ||||
|                 path: path('/hero.php', { t: 3 }), | ||||
|             }), | ||||
|             new Command(SendOnAdventureAction.name, args), | ||||
|             new Command(ClickButtonAction.name, { | ||||
|   | ||||
| @@ -4,13 +4,17 @@ import { TaskController, registerTask } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { TrainTrooperAction } from '../Action/TrainTrooperAction'; | ||||
| import { path } from '../utils'; | ||||
|  | ||||
| @registerTask | ||||
| export class TrainTroopTask extends TaskController { | ||||
|     async run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         const args: Args = { ...task.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.name, { ...args, path: '/build.php?id=' + args.buildId }), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/build.php', { newdid: args.villageId, id: args.buildId }), | ||||
|             }), | ||||
|             new Command(TrainTrooperAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|   | ||||
| @@ -5,17 +5,21 @@ import { TaskController, registerTask } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CheckBuildingRemainingTimeAction } from '../Action/CheckBuildingRemainingTimeAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { path } from '../utils'; | ||||
|  | ||||
| @registerTask | ||||
| export class UpgradeBuildingTask extends TaskController { | ||||
|     async run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         const args: Args = { ...task.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.name, { ...args, path: '/dorf1.php' }), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/dorf1.php', { newdid: args.villageId }), | ||||
|             }), | ||||
|             new Command(CheckBuildingRemainingTimeAction.name, args), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: '/build.php?id=' + args.id, | ||||
|                 path: path('/build.php', { newdid: args.villageId, id: args.buildId }), | ||||
|             }), | ||||
|             new Command(UpgradeBuildingAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|   | ||||
| @@ -25,7 +25,7 @@ export class TaskQueueRenderer { | ||||
|         tasks.forEach(task => { | ||||
|             ul.append( | ||||
|                 jQuery('<li></li>').text( | ||||
|                     formatDate(task.ts) + ' ' + task.cmd.name + ' ' + JSON.stringify(task.cmd.args) + ' ' + task.id | ||||
|                     formatDate(task.ts) + ' ' + task.name + ' ' + JSON.stringify(task.args) + ' ' + task.id | ||||
|                 ) | ||||
|             ); | ||||
|         }); | ||||
|   | ||||
| @@ -71,6 +71,14 @@ export function getNumber(value: any, def: number = 0): number { | ||||
|     return converted === undefined ? def : converted; | ||||
| } | ||||
|  | ||||
| export function path(p: string, query: { [key: string]: string | number } = {}) { | ||||
|     let parts: string[] = []; | ||||
|     for (let k in query) { | ||||
|         parts.push(`${k}=${query[k]}`); | ||||
|     } | ||||
|     return p + (parts.length ? '?' + parts.join('&') : ''); | ||||
| } | ||||
|  | ||||
| export function markPage(text: string, version: string) { | ||||
|     jQuery('body').append( | ||||
|         '<div style="' + | ||||
|   | ||||
		Reference in New Issue
	
	Block a user