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 { 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);
}

View File

@ -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, {
buildId,
troopId,
trainCount: n,
})
);
this.scheduler.scheduleTask(TrainTroopTask.name, {
villageId,
buildId,
troopId,
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 = {}) {
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 {

View File

@ -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;
}

View File

@ -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),
]);

View File

@ -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, {

View File

@ -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),
]);

View File

@ -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),

View File

@ -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
)
);
});

View File

@ -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="' +