Fix insert build tasks, so order is matter
This commit is contained in:
parent
db16c54137
commit
57ccf14553
@ -1,13 +1,14 @@
|
|||||||
import { ActionController, registerAction } from './ActionController';
|
import { ActionController, registerAction } from './ActionController';
|
||||||
import { Args } from '../Common';
|
import { Args } from '../Common';
|
||||||
import { Task } from '../Storage/TaskQueue';
|
import { Task } from '../Storage/TaskQueue';
|
||||||
import { BuildingQueueFullError } from '../Errors';
|
import { BuildingQueueFullError, GrabError } from '../Errors';
|
||||||
import { grabActiveVillageId, grabBuildingQueueInfo } from '../Page/VillageBlock';
|
import { grabActiveVillageId, grabBuildingQueueInfo } from '../Page/VillageBlock';
|
||||||
|
import { BuildingQueueInfo } from '../Game';
|
||||||
|
|
||||||
@registerAction
|
@registerAction
|
||||||
export class CheckBuildingRemainingTimeAction extends ActionController {
|
export class CheckBuildingRemainingTimeAction extends ActionController {
|
||||||
async run(args: Args, task: Task): Promise<any> {
|
async run(args: Args, task: Task): Promise<any> {
|
||||||
const info = grabBuildingQueueInfo();
|
const info = this.grabBuildingQueueInfoOrDefault();
|
||||||
if (info.seconds > 0) {
|
if (info.seconds > 0) {
|
||||||
throw new BuildingQueueFullError(
|
throw new BuildingQueueFullError(
|
||||||
task.id,
|
task.id,
|
||||||
@ -17,4 +18,15 @@ export class CheckBuildingRemainingTimeAction extends ActionController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private grabBuildingQueueInfoOrDefault() {
|
||||||
|
try {
|
||||||
|
return grabBuildingQueueInfo();
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof GrabError) {
|
||||||
|
return new BuildingQueueInfo(0);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { clickUpgradeButton } from '../Page/BuildingPage';
|
|||||||
import { grabResourceDeposits } from '../Page/SlotBlock';
|
import { grabResourceDeposits } from '../Page/SlotBlock';
|
||||||
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
||||||
import { ResourceDeposit } from '../Game';
|
import { ResourceDeposit } from '../Game';
|
||||||
|
import { aroundMinutes } from '../utils';
|
||||||
|
|
||||||
@registerAction
|
@registerAction
|
||||||
export class UpgradeResourceToLevel extends ActionController {
|
export class UpgradeResourceToLevel extends ActionController {
|
||||||
@ -35,19 +36,16 @@ export class UpgradeResourceToLevel extends ActionController {
|
|||||||
task.args.buildId === dep.buildId
|
task.args.buildId === dep.buildId
|
||||||
);
|
);
|
||||||
|
|
||||||
const available = deposits
|
const notUpgraded = deposits.sort((x, y) => x.level - y.level).filter(isDepositTaskNotInQueue);
|
||||||
.sort((x, y) => x.level - y.level)
|
|
||||||
.filter(dep => dep.ready)
|
|
||||||
.filter(isDepositTaskNotInQueue);
|
|
||||||
|
|
||||||
if (available.length === 0) {
|
if (notUpgraded.length === 0) {
|
||||||
throw new TryLaterError(task.id, 10 * 60, 'No available deposits');
|
throw new TryLaterError(task.id, aroundMinutes(10), 'No available deposits');
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetDep = available[0];
|
for (let dep of notUpgraded) {
|
||||||
|
this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId: dep.buildId });
|
||||||
|
}
|
||||||
|
|
||||||
this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId: targetDep.buildId });
|
throw new TryLaterError(task.id, aroundMinutes(10), 'Sleep for next round');
|
||||||
|
|
||||||
throw new TryLaterError(task.id, 20 * 60, 'Sleep for next round');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import { ResourcesInterface } from './Game';
|
||||||
|
|
||||||
export interface Args {
|
export interface Args {
|
||||||
villageId?: number;
|
villageId?: number;
|
||||||
buildId?: number;
|
buildId?: number;
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
buildTypeId?: number;
|
buildTypeId?: number;
|
||||||
|
resources?: ResourcesInterface;
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,14 @@ export function numberToResourceType(typeAsNumber: number): ResourceType {
|
|||||||
|
|
||||||
export type ResourceList = Array<{ num: number; type: ResourceType; value: number }>;
|
export type ResourceList = Array<{ num: number; type: ResourceType; value: number }>;
|
||||||
|
|
||||||
export class Resources {
|
export interface ResourcesInterface {
|
||||||
|
lumber: number;
|
||||||
|
clay: number;
|
||||||
|
iron: number;
|
||||||
|
crop: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Resources implements ResourcesInterface {
|
||||||
readonly lumber: number;
|
readonly lumber: number;
|
||||||
readonly clay: number;
|
readonly clay: number;
|
||||||
readonly iron: number;
|
readonly iron: number;
|
||||||
|
@ -4,7 +4,7 @@ import { Scheduler } from '../Scheduler';
|
|||||||
import { TrainTroopTask } from '../Task/TrainTroopTask';
|
import { TrainTroopTask } from '../Task/TrainTroopTask';
|
||||||
import { grabActiveVillageId } from './VillageBlock';
|
import { grabActiveVillageId } from './VillageBlock';
|
||||||
import { ConsoleLogger, Logger } from '../Logger';
|
import { ConsoleLogger, Logger } from '../Logger';
|
||||||
import { createBuildButton, createUpgradeButton } from './BuildingPage';
|
import { createBuildButton, createUpgradeButton, grabContractResources } from './BuildingPage';
|
||||||
import { BuildBuildingTask } from '../Task/BuildBuildingTask';
|
import { BuildBuildingTask } from '../Task/BuildBuildingTask';
|
||||||
|
|
||||||
const QUARTERS_ID = 19;
|
const QUARTERS_ID = 19;
|
||||||
@ -38,14 +38,16 @@ export class BuildPage {
|
|||||||
const buildId = this.buildId;
|
const buildId = this.buildId;
|
||||||
const categoryId = this.categoryId;
|
const categoryId = this.categoryId;
|
||||||
const villageId = grabActiveVillageId();
|
const villageId = grabActiveVillageId();
|
||||||
this.scheduler.scheduleTask(BuildBuildingTask.name, { villageId, buildId, categoryId, buildTypeId });
|
const resources = grabContractResources();
|
||||||
|
this.scheduler.scheduleTask(BuildBuildingTask.name, { villageId, buildId, categoryId, buildTypeId, resources });
|
||||||
notify(`Building ${buildId} scheduled`);
|
notify(`Building ${buildId} scheduled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onScheduleUpgradeBuilding() {
|
private onScheduleUpgradeBuilding() {
|
||||||
const buildId = this.buildId;
|
const buildId = this.buildId;
|
||||||
const villageId = grabActiveVillageId();
|
const villageId = grabActiveVillageId();
|
||||||
this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId });
|
const resources = grabContractResources();
|
||||||
|
this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId, resources });
|
||||||
notify(`Upgrading ${buildId} scheduled`);
|
notify(`Upgrading ${buildId} scheduled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { GrabError } from '../Errors';
|
import { GrabError } from '../Errors';
|
||||||
import { getNumber, trimPrefix, uniqId } from '../utils';
|
import { getNumber, trimPrefix, uniqId } from '../utils';
|
||||||
|
import { Resources } from '../Game';
|
||||||
|
|
||||||
export function clickBuildButton(typeId: number) {
|
export function clickBuildButton(typeId: number) {
|
||||||
const section = jQuery(`#contract_building${typeId}`);
|
const section = jQuery(`#contract_building${typeId}`);
|
||||||
@ -27,6 +28,11 @@ export function createBuildButton(onClickHandler: (buildTypeId: number) => void)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasUpgradeButton(): boolean {
|
||||||
|
const btn = jQuery('.upgradeButtonsContainer .section1 button.green.build');
|
||||||
|
return btn.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
export function clickUpgradeButton() {
|
export function clickUpgradeButton() {
|
||||||
const btn = jQuery('.upgradeButtonsContainer .section1 button.green.build');
|
const btn = jQuery('.upgradeButtonsContainer .section1 button.green.build');
|
||||||
if (btn.length !== 1) {
|
if (btn.length !== 1) {
|
||||||
@ -45,3 +51,17 @@ export function createUpgradeButton(onClickHandler: () => void) {
|
|||||||
onClickHandler();
|
onClickHandler();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function grabContractResources(): Resources {
|
||||||
|
const $els = jQuery('#contract .resource');
|
||||||
|
if ($els.length === 0) {
|
||||||
|
throw new GrabError('No resource contract element');
|
||||||
|
}
|
||||||
|
const grab = n =>
|
||||||
|
getNumber(
|
||||||
|
jQuery($els.get(n))
|
||||||
|
.find('.value')
|
||||||
|
.text()
|
||||||
|
);
|
||||||
|
return new Resources(grab(0), grab(1), grab(2), grab(3));
|
||||||
|
}
|
||||||
|
@ -47,7 +47,7 @@ function grabVillageInfo($el): Village {
|
|||||||
export function grabResourcesPerformance(): Resources {
|
export function grabResourcesPerformance(): Resources {
|
||||||
const $el = jQuery('#production');
|
const $el = jQuery('#production');
|
||||||
if ($el.length !== 1) {
|
if ($el.length !== 1) {
|
||||||
throw new GrabError();
|
throw new GrabError('No production element');
|
||||||
}
|
}
|
||||||
|
|
||||||
const $nums = $el.find('td.num');
|
const $nums = $el.find('td.num');
|
||||||
@ -63,7 +63,7 @@ export function grabResourcesPerformance(): Resources {
|
|||||||
export function grabBuildingQueueInfo(): BuildingQueueInfo {
|
export function grabBuildingQueueInfo(): BuildingQueueInfo {
|
||||||
const timer = jQuery('.buildDuration .timer');
|
const timer = jQuery('.buildDuration .timer');
|
||||||
if (timer.length !== 1) {
|
if (timer.length !== 1) {
|
||||||
throw new GrabError();
|
throw new GrabError('No building queue timer element');
|
||||||
}
|
}
|
||||||
|
|
||||||
const remainingSeconds = getNumber(timer.attr('value'));
|
const remainingSeconds = getNumber(timer.attr('value'));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { timestamp } from './utils';
|
import { timestamp } from './utils';
|
||||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
import { TaskId, TaskList, TaskQueue } from './Storage/TaskQueue';
|
import { Task, TaskId, TaskList, TaskQueue } from './Storage/TaskQueue';
|
||||||
import { Args, Command } from './Common';
|
import { Args, Command } from './Common';
|
||||||
import { SendOnAdventureTask } from './Task/SendOnAdventureTask';
|
import { SendOnAdventureTask } from './Task/SendOnAdventureTask';
|
||||||
import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask';
|
import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask';
|
||||||
@ -50,7 +50,17 @@ export class Scheduler {
|
|||||||
|
|
||||||
scheduleTask(name: string, args: Args): void {
|
scheduleTask(name: string, args: Args): void {
|
||||||
this.logger.log('PUSH TASK', name, args);
|
this.logger.log('PUSH TASK', name, args);
|
||||||
this.taskQueue.push(name, args, timestamp());
|
const villageId = args.villageId;
|
||||||
|
if (isBuildingTask(name)) {
|
||||||
|
this.taskQueue.insertAfterLast(
|
||||||
|
t => isBuildingTask(t.name) && sameVillage(villageId, t.args),
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
timestamp()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.taskQueue.push(name, args, timestamp());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
completeTask(id: TaskId) {
|
completeTask(id: TaskId) {
|
||||||
@ -69,11 +79,7 @@ export class Scheduler {
|
|||||||
|
|
||||||
postponeBuildingsInVillage(villageId: number, seconds: number) {
|
postponeBuildingsInVillage(villageId: number, seconds: number) {
|
||||||
this.taskQueue.modify(
|
this.taskQueue.modify(
|
||||||
t => t.name === BuildBuildingTask.name && t.args.villageId === villageId,
|
t => isBuildingTask(t.name) && sameVillage(villageId, t.args),
|
||||||
t => t.withTime(timestamp() + seconds)
|
|
||||||
);
|
|
||||||
this.taskQueue.modify(
|
|
||||||
t => t.name === UpgradeBuildingTask.name && t.args.villageId === villageId,
|
|
||||||
t => t.withTime(timestamp() + seconds)
|
t => t.withTime(timestamp() + seconds)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -86,3 +92,11 @@ export class Scheduler {
|
|||||||
this.actionQueue.clear();
|
this.actionQueue.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBuildingTask(taskName: string) {
|
||||||
|
return taskName === BuildBuildingTask.name || taskName === UpgradeBuildingTask.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameVillage(villageId: number | undefined, args: Args) {
|
||||||
|
return villageId !== undefined && args.villageId === villageId;
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ import { StateGrabber } from './StateGrabber';
|
|||||||
import { grabActiveVillageId, grabBuildingQueueInfo, grabResourcesPerformance } from '../Page/VillageBlock';
|
import { grabActiveVillageId, grabBuildingQueueInfo, grabResourcesPerformance } from '../Page/VillageBlock';
|
||||||
import { VillageState } from './VillageState';
|
import { VillageState } from './VillageState';
|
||||||
import { parseLocation } from '../utils';
|
import { parseLocation } from '../utils';
|
||||||
|
import { GrabError } from '../Errors';
|
||||||
|
import { BuildingQueueInfo } from '../Game';
|
||||||
|
|
||||||
export class VillageOverviewPageGrabber extends StateGrabber {
|
export class VillageOverviewPageGrabber extends StateGrabber {
|
||||||
grab(): void {
|
grab(): void {
|
||||||
@ -13,6 +15,17 @@ export class VillageOverviewPageGrabber extends StateGrabber {
|
|||||||
const villageId = grabActiveVillageId();
|
const villageId = grabActiveVillageId();
|
||||||
const state = new VillageState(villageId);
|
const state = new VillageState(villageId);
|
||||||
state.storeResourcesPerformance(grabResourcesPerformance());
|
state.storeResourcesPerformance(grabResourcesPerformance());
|
||||||
state.storeBuildingQueueInfo(grabBuildingQueueInfo());
|
state.storeBuildingQueueInfo(this.grabBuildingQueueInfoOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
private grabBuildingQueueInfoOrDefault() {
|
||||||
|
try {
|
||||||
|
return grabBuildingQueueInfo();
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof GrabError) {
|
||||||
|
return new BuildingQueueInfo(0);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,15 @@ export class TaskQueue {
|
|||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertAfterLast(predicate: (t: Task) => boolean, name: string, args: Args, ts: number): Task {
|
||||||
|
const id = uniqTaskId();
|
||||||
|
const task = new Task(id, ts, name, args);
|
||||||
|
this.logger.log('INSERT AFTER TASK', id, ts, name, args);
|
||||||
|
const items = insertTaskAfter(task, this.getItems(), predicate);
|
||||||
|
this.flushItems(items);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
get(ts: number): Task | undefined {
|
get(ts: number): Task | undefined {
|
||||||
const readyItems = this.getItems().filter(t => t.ts <= ts);
|
const readyItems = this.getItems().filter(t => t.ts <= ts);
|
||||||
if (readyItems.length === 0) {
|
if (readyItems.length === 0) {
|
||||||
@ -133,3 +142,27 @@ export class TaskQueue {
|
|||||||
this.storage.set(QUEUE_NAME, normalized);
|
this.storage.set(QUEUE_NAME, normalized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertTaskAfter(task: Task, tasks: TaskList, predicate: (t: Task) => boolean): TaskList {
|
||||||
|
const queuedTaskIndex = findLastIndex(tasks, predicate);
|
||||||
|
if (queuedTaskIndex === undefined) {
|
||||||
|
tasks.push(task);
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
const queuedTask = tasks[queuedTaskIndex];
|
||||||
|
let insertedTask = task.withTime(Math.max(task.ts, queuedTask.ts + 1));
|
||||||
|
tasks.splice(queuedTaskIndex, 0, insertedTask);
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLastIndex(tasks: TaskList, predicate: (t: Task) => boolean): number | undefined {
|
||||||
|
const count = tasks.length;
|
||||||
|
const indexInReversed = tasks
|
||||||
|
.slice()
|
||||||
|
.reverse()
|
||||||
|
.findIndex(predicate);
|
||||||
|
if (indexInReversed < 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return count - 1 - indexInReversed;
|
||||||
|
}
|
||||||
|
@ -22,6 +22,12 @@ export async function sleepLong() {
|
|||||||
return await sleep(ms);
|
return await sleep(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function aroundMinutes(minutes: number) {
|
||||||
|
const seconds = minutes * 60;
|
||||||
|
const delta = Math.floor(seconds * 0.9);
|
||||||
|
return seconds - delta + Math.floor(Math.random() * 2 * delta);
|
||||||
|
}
|
||||||
|
|
||||||
export async function waitForLoad() {
|
export async function waitForLoad() {
|
||||||
return new Promise(resolve => jQuery(resolve));
|
return new Promise(resolve => jQuery(resolve));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user