Add multiple village support

This commit is contained in:
Anton Vakhrushev 2020-04-10 18:32:51 +03:00
parent bf7f4c1b7d
commit 3a673ac334
11 changed files with 120 additions and 72 deletions

View File

@ -1,9 +1,10 @@
import * as URLParse from 'url-parse'; import * as URLParse from 'url-parse';
import { elClassId, markPage, waitForLoad } from '../utils'; import { markPage, waitForLoad } from '../utils';
import { Scheduler } from '../Scheduler'; import { Scheduler } from '../Scheduler';
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
import { TaskQueueRenderer } from '../TaskQueueRenderer'; 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 { export class Dashboard {
private readonly version: string; private readonly version: string;
@ -24,12 +25,19 @@ export class Dashboard {
this.renderTaskQueue(); this.renderTaskQueue();
setInterval(() => this.renderTaskQueue(), 5000); 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') { if (p.pathname === '/dorf1.php') {
this.showSlotIds('buildingSlot'); showFieldsSlotIds(buildingsInQueue);
} }
if (p.pathname === '/dorf2.php') { if (p.pathname === '/dorf2.php') {
this.showSlotIds('aid'); showBuildingSlotIds(buildingsInQueue);
} }
if (p.pathname === '/build.php') { if (p.pathname === '/build.php') {
@ -42,27 +50,6 @@ export class Dashboard {
new TaskQueueRenderer().render(this.scheduler.getTaskItems()); 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) { private log(...args) {
console.log('SCHEDULER:', ...args); console.log('SCHEDULER:', ...args);
} }

View File

@ -1,8 +1,8 @@
import { elClassId, split, uniqId } from '../utils'; import { elClassId, split, uniqId } from '../utils';
import { Command } from '../Common';
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
import { Scheduler } from '../Scheduler'; import { Scheduler } from '../Scheduler';
import { TrainTroopTask } from '../Task/TrainTroopTask'; import { TrainTroopTask } from '../Task/TrainTroopTask';
import { grabActiveVillageId } from './EveryPage';
const QUARTERS_ID = 19; const QUARTERS_ID = 19;
@ -34,12 +34,10 @@ export class BuildPage {
}); });
} }
private onScheduleBuilding(id: number) { private onScheduleBuilding(buildId: number) {
const queueItem = new Command(UpgradeBuildingTask.name, { const villageId = grabActiveVillageId();
id, this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId });
}); const n = new Notification(`Building ${buildId} scheduled`);
this.scheduler.scheduleTask(queueItem);
const n = new Notification(`Building ${id} scheduled`);
setTimeout(() => n && n.close(), 4000); setTimeout(() => n && n.close(), 4000);
} }
@ -63,18 +61,18 @@ export class BuildPage {
private onTrainTroopClick(buildId: Number, troopId: Number, el: HTMLElement) { private onTrainTroopClick(buildId: Number, troopId: Number, el: HTMLElement) {
console.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId); console.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId);
const villageId = grabActiveVillageId();
const input = jQuery(el).find(`input[name="t${troopId}"]`); const input = jQuery(el).find(`input[name="t${troopId}"]`);
const count = Number(input.val()); const count = Number(input.val());
if (!isNaN(count) && count > 0) { if (!isNaN(count) && count > 0) {
console.log('PREPARE TO TRAIN', count, 'TROOPERS'); console.log('PREPARE TO TRAIN', count, 'TROOPERS');
for (let n of split(count)) { for (let n of split(count)) {
this.scheduler.scheduleTask( this.scheduler.scheduleTask(TrainTroopTask.name, {
new Command(TrainTroopTask.name, { villageId,
buildId, buildId,
troopId, troopId,
trainCount: n, trainCount: n,
}) });
);
} }
} }
} }

35
src/Page/EveryPage.ts Normal file
View 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);
}

View File

@ -47,7 +47,7 @@ export class Scheduler {
private scheduleUniqTask(seconds: number, name: string, args: Args = {}) { private scheduleUniqTask(seconds: number, name: string, args: Args = {}) {
const taskScheduler = () => { const taskScheduler = () => {
if (!this.taskQueue.hasNamed(name)) { if (!this.taskQueue.hasNamed(name)) {
this.taskQueue.push(new Command(name, args), timestamp() + 10 * 60); this.taskQueue.push(name, args, timestamp() + 10 * 60);
} }
}; };
taskScheduler(); taskScheduler();
@ -95,12 +95,12 @@ export class Scheduler {
} }
private async processTaskCommand(task: Task) { private async processTaskCommand(task: Task) {
const taskController = createTask(task.cmd.name, this); const taskController = createTask(task.name, this);
this.log('PROCESS TASK', task.cmd.name, task, taskController); this.log('PROCESS TASK', task.name, task, taskController);
if (taskController) { if (taskController) {
await taskController.run(task); await taskController.run(task);
} else { } else {
this.logWarn('TASK NOT FOUND', task.cmd.name); this.logWarn('TASK NOT FOUND', task.name);
this.taskQueue.complete(task.id); this.taskQueue.complete(task.id);
} }
} }
@ -123,7 +123,7 @@ export class Scheduler {
if (err instanceof BuildingQueueFullError) { if (err instanceof BuildingQueueFullError) {
this.logWarn('BUILDING QUEUE FULL, TRY ALL AFTER', err.seconds); this.logWarn('BUILDING QUEUE FULL, TRY ALL AFTER', err.seconds);
this.taskQueue.modify( this.taskQueue.modify(
t => t.cmd.name === UpgradeBuildingTask.name, t => t.name === UpgradeBuildingTask.name,
t => t.withTime(timestamp() + err.seconds) t => t.withTime(timestamp() + err.seconds)
); );
return; return;
@ -146,9 +146,9 @@ export class Scheduler {
this.taskQueue.complete(id); this.taskQueue.complete(id);
} }
scheduleTask(task: Command): void { scheduleTask(name: string, args: Args): void {
this.log('PUSH TASK', task); this.log('PUSH TASK', name, args);
this.taskQueue.push(task, timestamp()); this.taskQueue.push(name, args, timestamp());
} }
scheduleActions(actions: Array<Command>): void { scheduleActions(actions: Array<Command>): void {

View File

@ -1,7 +1,7 @@
import { Command } from '../Common'; import { Args } from '../Common';
import { uniqId } from '../utils'; import { uniqId } from '../utils';
const QUEUE_NAME = 'task_queue:v3'; const QUEUE_NAME = 'task_queue:v4';
export type TaskId = string; export type TaskId = string;
@ -12,25 +12,27 @@ function uniqTaskId(): TaskId {
export class Task { export class Task {
readonly id: TaskId; readonly id: TaskId;
readonly ts: number; readonly ts: number;
readonly cmd: Command; readonly name: string;
constructor(id: TaskId, ts: number, cmd: Command) { readonly args: Args;
constructor(id: TaskId, ts: number, name: string, args: Args) {
this.id = id; this.id = id;
this.ts = ts; this.ts = ts;
this.cmd = cmd; this.name = name;
this.args = args;
} }
withTime(ts: number): Task { 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 type TaskList = Array<Task>;
export class TaskQueue { export class TaskQueue {
push(cmd: Command, ts: number): Task { push(name: string, args: Args, ts: number): Task {
const id = uniqTaskId(); const id = uniqTaskId();
const task = new Task(id, ts, cmd); const task = new Task(id, ts, name, args);
this.log('PUSH TASK', id, ts, cmd); this.log('PUSH TASK', id, ts, name, args);
let items = this.getItems(); let items = this.getItems();
items.push(task); items.push(task);
this.flushItems(items); this.flushItems(items);
@ -51,7 +53,7 @@ export class TaskQueue {
} }
hasNamed(name: string): boolean { 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) { 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 storedItems = serialized !== null ? (JSON.parse(serialized) as Array<{ [key: string]: any }>) : [];
const items: TaskList = []; const items: TaskList = [];
storedItems.forEach(obj => { 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; return items;
} }

View File

@ -3,18 +3,24 @@ import { Task } from '../Storage/TaskQueue';
import { TaskController, registerTask } from './TaskController'; import { TaskController, registerTask } from './TaskController';
import { GoToPageAction } from '../Action/GoToPageAction'; import { GoToPageAction } from '../Action/GoToPageAction';
import { CompleteTaskAction } from '../Action/CompleteTaskAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction';
import { TrainTrooperAction } from '../Action/TrainTrooperAction';
import { GrabVillageResourcesAction } from '../Action/GrabVillageResourcesAction'; import { GrabVillageResourcesAction } from '../Action/GrabVillageResourcesAction';
import { BalanceHeroResourcesAction } from '../Action/BalanceHeroResourcesAction'; import { BalanceHeroResourcesAction } from '../Action/BalanceHeroResourcesAction';
import { path } from '../utils';
@registerTask @registerTask
export class BalanceHeroResourcesTask extends TaskController { export class BalanceHeroResourcesTask extends TaskController {
async run(task: Task) { async run(task: Task) {
const args: Args = { ...task.cmd.args, taskId: task.id }; const args: Args = { ...task.args, taskId: task.id };
this.scheduler.scheduleActions([ 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(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(BalanceHeroResourcesAction.name, args),
new Command(CompleteTaskAction.name, args), new Command(CompleteTaskAction.name, args),
]); ]);

View File

@ -6,17 +6,21 @@ import { GrabHeroAttributesAction } from '../Action/GrabHeroAttributesAction';
import { CompleteTaskAction } from '../Action/CompleteTaskAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction';
import { SendOnAdventureAction } from '../Action/SendOnAdventureAction'; import { SendOnAdventureAction } from '../Action/SendOnAdventureAction';
import { ClickButtonAction } from '../Action/ClickButtonAction'; import { ClickButtonAction } from '../Action/ClickButtonAction';
import { path } from '../utils';
@registerTask @registerTask
export class SendOnAdventureTask extends TaskController { export class SendOnAdventureTask extends TaskController {
async run(task: Task) { async run(task: Task) {
const args: Args = { ...task.cmd.args, taskId: task.id }; const args: Args = { ...task.args, taskId: task.id };
this.scheduler.scheduleActions([ 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(GrabHeroAttributesAction.name, args),
new Command(GoToPageAction.name, { new Command(GoToPageAction.name, {
...args, ...args,
path: '/hero.php?t=3', path: path('/hero.php', { t: 3 }),
}), }),
new Command(SendOnAdventureAction.name, args), new Command(SendOnAdventureAction.name, args),
new Command(ClickButtonAction.name, { new Command(ClickButtonAction.name, {

View File

@ -4,13 +4,17 @@ import { TaskController, registerTask } from './TaskController';
import { GoToPageAction } from '../Action/GoToPageAction'; import { GoToPageAction } from '../Action/GoToPageAction';
import { CompleteTaskAction } from '../Action/CompleteTaskAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction';
import { TrainTrooperAction } from '../Action/TrainTrooperAction'; import { TrainTrooperAction } from '../Action/TrainTrooperAction';
import { path } from '../utils';
@registerTask @registerTask
export class TrainTroopTask extends TaskController { export class TrainTroopTask extends TaskController {
async run(task: Task) { async run(task: Task) {
const args: Args = { ...task.cmd.args, taskId: task.id }; const args: Args = { ...task.args, taskId: task.id };
this.scheduler.scheduleActions([ 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(TrainTrooperAction.name, args),
new Command(CompleteTaskAction.name, args), new Command(CompleteTaskAction.name, args),
]); ]);

View File

@ -5,17 +5,21 @@ import { TaskController, registerTask } from './TaskController';
import { GoToPageAction } from '../Action/GoToPageAction'; import { GoToPageAction } from '../Action/GoToPageAction';
import { CheckBuildingRemainingTimeAction } from '../Action/CheckBuildingRemainingTimeAction'; import { CheckBuildingRemainingTimeAction } from '../Action/CheckBuildingRemainingTimeAction';
import { CompleteTaskAction } from '../Action/CompleteTaskAction'; import { CompleteTaskAction } from '../Action/CompleteTaskAction';
import { path } from '../utils';
@registerTask @registerTask
export class UpgradeBuildingTask extends TaskController { export class UpgradeBuildingTask extends TaskController {
async run(task: Task) { async run(task: Task) {
const args: Args = { ...task.cmd.args, taskId: task.id }; const args: Args = { ...task.args, taskId: task.id };
this.scheduler.scheduleActions([ 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(CheckBuildingRemainingTimeAction.name, args),
new Command(GoToPageAction.name, { new Command(GoToPageAction.name, {
...args, ...args,
path: '/build.php?id=' + args.id, path: path('/build.php', { newdid: args.villageId, id: args.buildId }),
}), }),
new Command(UpgradeBuildingAction.name, args), new Command(UpgradeBuildingAction.name, args),
new Command(CompleteTaskAction.name, args), new Command(CompleteTaskAction.name, args),

View File

@ -25,7 +25,7 @@ export class TaskQueueRenderer {
tasks.forEach(task => { tasks.forEach(task => {
ul.append( ul.append(
jQuery('<li></li>').text( 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
) )
); );
}); });

View File

@ -71,6 +71,14 @@ export function getNumber(value: any, def: number = 0): number {
return converted === undefined ? def : converted; 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) { export function markPage(text: string, version: string) {
jQuery('body').append( jQuery('body').append(
'<div style="' + '<div style="' +