Improve village state management
This commit is contained in:
parent
8bea617f5b
commit
301b1a6ca9
@ -5,7 +5,7 @@ import { aroundMinutes } from '../utils';
|
|||||||
import { Args } from '../Queue/Args';
|
import { Args } from '../Queue/Args';
|
||||||
import { Task } from '../Queue/TaskProvider';
|
import { Task } from '../Queue/TaskProvider';
|
||||||
import { VillageStorage } from '../Storage/VillageStorage';
|
import { VillageStorage } from '../Storage/VillageStorage';
|
||||||
import { VillageStateRepository } from '../VillageState';
|
import { VillageFactory } from '../VillageFactory';
|
||||||
|
|
||||||
const actionMap: { [name: string]: Function | undefined } = {};
|
const actionMap: { [name: string]: Function | undefined } = {};
|
||||||
|
|
||||||
@ -16,22 +16,22 @@ export function registerAction(constructor: Function) {
|
|||||||
export function createActionHandler(
|
export function createActionHandler(
|
||||||
name: string,
|
name: string,
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
villageStateRepository: VillageStateRepository
|
villageFactory: VillageFactory
|
||||||
): ActionController | undefined {
|
): ActionController | undefined {
|
||||||
const storedFunction = actionMap[name];
|
const storedFunction = actionMap[name];
|
||||||
if (storedFunction === undefined) {
|
if (storedFunction === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const constructor = (storedFunction as unknown) as typeof ActionController;
|
const constructor = (storedFunction as unknown) as typeof ActionController;
|
||||||
return new constructor(scheduler, villageStateRepository);
|
return new constructor(scheduler, villageFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ActionController {
|
export class ActionController {
|
||||||
protected scheduler: Scheduler;
|
protected readonly scheduler: Scheduler;
|
||||||
protected villageStateRepository: VillageStateRepository;
|
protected readonly villageFactory: VillageFactory;
|
||||||
constructor(scheduler: Scheduler, villageStateRepository: VillageStateRepository) {
|
constructor(scheduler: Scheduler, villageFactory: VillageFactory) {
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.villageStateRepository = villageStateRepository;
|
this.villageFactory = villageFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(args: Args, task: Task) {}
|
async run(args: Args, task: Task) {}
|
||||||
|
@ -18,7 +18,7 @@ export class BalanceHeroResourcesAction extends ActionController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisVillageState = this.villageStateRepository.getVillageState(thisVillageId);
|
const thisVillageState = this.villageFactory.createState(thisVillageId);
|
||||||
|
|
||||||
const requirements = [
|
const requirements = [
|
||||||
thisVillageState.required.balance,
|
thisVillageState.required.balance,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ActionController, registerAction } from './ActionController';
|
import { ActionController, registerAction } from './ActionController';
|
||||||
import { AbortTaskError, taskError } from '../Errors';
|
import { taskError } from '../Errors';
|
||||||
import { Args } from '../Queue/Args';
|
import { Args } from '../Queue/Args';
|
||||||
import { Task } from '../Queue/TaskProvider';
|
import { Task } from '../Queue/TaskProvider';
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ export class SendResourcesAction extends ActionController {
|
|||||||
|
|
||||||
const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates'));
|
const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates'));
|
||||||
|
|
||||||
const senderVillage = this.villageStateRepository.getVillageState(senderVillageId);
|
const senderVillage = this.villageFactory.createState(senderVillageId);
|
||||||
const recipientVillage = this.villageStateRepository.getVillageState(targetVillageId);
|
const recipientVillage = this.villageFactory.createState(targetVillageId);
|
||||||
|
|
||||||
const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage);
|
const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ActionController, registerAction } from './ActionController';
|
import { ActionController, registerAction } from './ActionController';
|
||||||
import { AbortTaskError, ActionError, taskError, TryLaterError } from '../Errors';
|
import { ActionError, taskError, TryLaterError } from '../Errors';
|
||||||
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';
|
||||||
|
@ -8,9 +8,8 @@ import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider';
|
|||||||
import { Statistics } from './Statistics';
|
import { Statistics } from './Statistics';
|
||||||
import { StatisticsStorage } from './Storage/StatisticsStorage';
|
import { StatisticsStorage } from './Storage/StatisticsStorage';
|
||||||
import { VillageRepository } from './VillageRepository';
|
import { VillageRepository } from './VillageRepository';
|
||||||
import { VillageStateRepository } from './VillageState';
|
|
||||||
import { LogStorage } from './Storage/LogStorage';
|
import { LogStorage } from './Storage/LogStorage';
|
||||||
import { VillageControllerFactory } from './VillageControllerFactory';
|
import { VillageFactory } from './VillageFactory';
|
||||||
import { GrabberManager } from './Grabber/GrabberManager';
|
import { GrabberManager } from './Grabber/GrabberManager';
|
||||||
|
|
||||||
export class Container {
|
export class Container {
|
||||||
@ -42,15 +41,15 @@ export class Container {
|
|||||||
return this._statistics;
|
return this._statistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _villageControllerFactory: VillageControllerFactory | undefined;
|
private _villageFactory: VillageFactory | undefined;
|
||||||
|
|
||||||
get villageControllerFactory(): VillageControllerFactory {
|
get villageFactory(): VillageFactory {
|
||||||
this._villageControllerFactory =
|
this._villageFactory =
|
||||||
this._villageControllerFactory ||
|
this._villageFactory ||
|
||||||
(() => {
|
(() => {
|
||||||
return new VillageControllerFactory(this.villageRepository);
|
return new VillageFactory(this.villageRepository);
|
||||||
})();
|
})();
|
||||||
return this._villageControllerFactory;
|
return this._villageFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _scheduler: Scheduler | undefined;
|
private _scheduler: Scheduler | undefined;
|
||||||
@ -68,31 +67,20 @@ export class Container {
|
|||||||
taskQueue,
|
taskQueue,
|
||||||
actionQueue,
|
actionQueue,
|
||||||
this.villageRepository,
|
this.villageRepository,
|
||||||
this.villageControllerFactory,
|
this.villageFactory,
|
||||||
new ConsoleLogger(Scheduler.name)
|
new ConsoleLogger(Scheduler.name)
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
return this._scheduler;
|
return this._scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _villageStateRepository: VillageStateRepository | undefined;
|
|
||||||
|
|
||||||
get villageStateRepository(): VillageStateRepository {
|
|
||||||
this._villageStateRepository =
|
|
||||||
this._villageStateRepository ||
|
|
||||||
(() => {
|
|
||||||
return new VillageStateRepository(this.villageRepository, this.villageControllerFactory);
|
|
||||||
})();
|
|
||||||
return this._villageStateRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _grabberManager: GrabberManager | undefined;
|
private _grabberManager: GrabberManager | undefined;
|
||||||
|
|
||||||
get grabberManager(): GrabberManager {
|
get grabberManager(): GrabberManager {
|
||||||
this._grabberManager =
|
this._grabberManager =
|
||||||
this._grabberManager ||
|
this._grabberManager ||
|
||||||
(() => {
|
(() => {
|
||||||
return new GrabberManager(this.villageControllerFactory);
|
return new GrabberManager(this.villageFactory);
|
||||||
})();
|
})();
|
||||||
return this._grabberManager;
|
return this._grabberManager;
|
||||||
}
|
}
|
||||||
@ -110,8 +98,7 @@ export class Container {
|
|||||||
return new Executor(
|
return new Executor(
|
||||||
this.version,
|
this.version,
|
||||||
this.scheduler,
|
this.scheduler,
|
||||||
this.villageStateRepository,
|
this.villageFactory,
|
||||||
this.villageControllerFactory,
|
|
||||||
this.grabberManager,
|
this.grabberManager,
|
||||||
this.statistics,
|
this.statistics,
|
||||||
logger
|
logger
|
||||||
@ -126,12 +113,7 @@ export class Container {
|
|||||||
this._controlPanel =
|
this._controlPanel =
|
||||||
this._controlPanel ||
|
this._controlPanel ||
|
||||||
(() => {
|
(() => {
|
||||||
return new ControlPanel(
|
return new ControlPanel(this.version, this.scheduler, this.villageFactory);
|
||||||
this.version,
|
|
||||||
this.scheduler,
|
|
||||||
this.villageStateRepository,
|
|
||||||
this.villageControllerFactory
|
|
||||||
);
|
|
||||||
})();
|
})();
|
||||||
return this._controlPanel;
|
return this._controlPanel;
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@ import { ConsoleLogger, Logger } from './Logger';
|
|||||||
import { DataStorage } from './DataStorage';
|
import { DataStorage } from './DataStorage';
|
||||||
import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors';
|
import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors';
|
||||||
import { ExecutionStorage } from './Storage/ExecutionStorage';
|
import { ExecutionStorage } from './Storage/ExecutionStorage';
|
||||||
import { VillageState, VillageStateRepository } from './VillageState';
|
import { VillageState } from './VillageState';
|
||||||
import { Task } from './Queue/TaskProvider';
|
import { Task } from './Queue/TaskProvider';
|
||||||
import { Action } from './Queue/ActionQueue';
|
import { Action } from './Queue/ActionQueue';
|
||||||
import { createStore } from './DashboardView/Store';
|
import { createStore } from './DashboardView/Store';
|
||||||
import { VillageControllerFactory } from './VillageControllerFactory';
|
import { VillageFactory } from './VillageFactory';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
@ -52,20 +52,13 @@ interface GameState {
|
|||||||
export class ControlPanel {
|
export class ControlPanel {
|
||||||
private readonly version: string;
|
private readonly version: string;
|
||||||
private readonly scheduler: Scheduler;
|
private readonly scheduler: Scheduler;
|
||||||
private readonly villageStateRepository: VillageStateRepository;
|
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
private villageControllerFactory: VillageControllerFactory;
|
private readonly villageFactory: VillageFactory;
|
||||||
|
|
||||||
constructor(
|
constructor(version: string, scheduler: Scheduler, villageFactory: VillageFactory) {
|
||||||
version: string,
|
|
||||||
scheduler: Scheduler,
|
|
||||||
villageStateRepository: VillageStateRepository,
|
|
||||||
villageControllerFactory: VillageControllerFactory
|
|
||||||
) {
|
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.villageStateRepository = villageStateRepository;
|
this.villageFactory = villageFactory;
|
||||||
this.villageControllerFactory = villageControllerFactory;
|
|
||||||
this.logger = new ConsoleLogger(this.constructor.name);
|
this.logger = new ConsoleLogger(this.constructor.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +71,7 @@ export class ControlPanel {
|
|||||||
const villageId = grabActiveVillageId();
|
const villageId = grabActiveVillageId();
|
||||||
|
|
||||||
const scheduler = this.scheduler;
|
const scheduler = this.scheduler;
|
||||||
const villageStateRepository = this.villageStateRepository;
|
const villageFactory = this.villageFactory;
|
||||||
|
|
||||||
const executionState = new ExecutionStorage();
|
const executionState = new ExecutionStorage();
|
||||||
|
|
||||||
@ -106,7 +99,7 @@ export class ControlPanel {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refreshVillages() {
|
refreshVillages() {
|
||||||
this.villageStates = villageStateRepository.getAllVillageStates();
|
this.villageStates = villageFactory.getAllVillageStates();
|
||||||
for (let state of this.villageStates) {
|
for (let state of this.villageStates) {
|
||||||
if (state.village.active) {
|
if (state.village.active) {
|
||||||
this.activeVillageState = state;
|
this.activeVillageState = state;
|
||||||
@ -136,8 +129,8 @@ export class ControlPanel {
|
|||||||
DataStorage.onChange(() => state.refresh());
|
DataStorage.onChange(() => state.refresh());
|
||||||
|
|
||||||
const getBuildingsInQueue = () =>
|
const getBuildingsInQueue = () =>
|
||||||
this.villageControllerFactory
|
this.villageFactory
|
||||||
.create(villageId)
|
.createTaskCollection(villageId)
|
||||||
.getTasks()
|
.getTasks()
|
||||||
.filter(t => t.name === UpgradeBuildingTask.name)
|
.filter(t => t.name === UpgradeBuildingTask.name)
|
||||||
.map(t => t.args.buildId || 0);
|
.map(t => t.args.buildId || 0);
|
||||||
@ -163,21 +156,21 @@ export class ControlPanel {
|
|||||||
const buildPage = new BuildingPageController(
|
const buildPage = new BuildingPageController(
|
||||||
this.scheduler,
|
this.scheduler,
|
||||||
getBuildingPageAttributes(),
|
getBuildingPageAttributes(),
|
||||||
this.villageControllerFactory.create(villageId)
|
this.villageFactory.createController(villageId)
|
||||||
);
|
);
|
||||||
buildPage.run();
|
buildPage.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createControlPanel(state, villageStateRepository);
|
this.createControlPanel(state, villageFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createControlPanel(gameState: GameState, villageStateRepository: VillageStateRepository) {
|
private createControlPanel(gameState: GameState, villageFactory: VillageFactory) {
|
||||||
const appId = `app-${uniqId()}`;
|
const appId = `app-${uniqId()}`;
|
||||||
jQuery('body').prepend(`<div id="${appId}"></div>`);
|
jQuery('body').prepend(`<div id="${appId}"></div>`);
|
||||||
new Vue({
|
new Vue({
|
||||||
el: `#${appId}`,
|
el: `#${appId}`,
|
||||||
data: gameState,
|
data: gameState,
|
||||||
store: createStore(villageStateRepository),
|
store: createStore(villageFactory),
|
||||||
render: h => h(DashboardApp),
|
render: h => h(DashboardApp),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import { LogStorage } from '../Storage/LogStorage';
|
import { LogStorage } from '../Storage/LogStorage';
|
||||||
import { VillageStateRepository } from '../VillageState';
|
|
||||||
import { VillageSettings, VillageSettingsDefaults } from '../Core/Village';
|
import { VillageSettings, VillageSettingsDefaults } from '../Core/Village';
|
||||||
import { getNumber, notify } from '../utils';
|
import { getNumber, notify } from '../utils';
|
||||||
import { VillageStorage } from '../Storage/VillageStorage';
|
import { VillageStorage } from '../Storage/VillageStorage';
|
||||||
|
import { VillageFactory } from '../VillageFactory';
|
||||||
|
|
||||||
export enum Mutations {
|
export enum Mutations {
|
||||||
showLogs = 'showLogs',
|
showLogs = 'showLogs',
|
||||||
@ -22,7 +22,7 @@ export enum Actions {
|
|||||||
SaveVillageSettings = 'save_village_settings',
|
SaveVillageSettings = 'save_village_settings',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createStore(villageStateRepository: VillageStateRepository) {
|
export function createStore(villageFactory: VillageFactory) {
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
views: {
|
views: {
|
||||||
@ -74,7 +74,7 @@ export function createStore(villageStateRepository: VillageStateRepository) {
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
[Actions.OpenVillageEditor]({ commit }, { villageId }) {
|
[Actions.OpenVillageEditor]({ commit }, { villageId }) {
|
||||||
const state = villageStateRepository.getVillageState(villageId);
|
const state = villageFactory.createState(villageId);
|
||||||
const settings = state.settings;
|
const settings = state.settings;
|
||||||
commit(Mutations.SetVillageSettings, {
|
commit(Mutations.SetVillageSettings, {
|
||||||
villageId: state.id,
|
villageId: state.id,
|
||||||
|
@ -10,8 +10,8 @@ import { ExecutionStorage } from './Storage/ExecutionStorage';
|
|||||||
import { Action } from './Queue/ActionQueue';
|
import { Action } from './Queue/ActionQueue';
|
||||||
import { Task } from './Queue/TaskProvider';
|
import { Task } from './Queue/TaskProvider';
|
||||||
import { createTaskHandler } from './Task/TaskMap';
|
import { createTaskHandler } from './Task/TaskMap';
|
||||||
import { VillageStateRepository } from './VillageState';
|
import { VillageStateFactory } from './VillageState';
|
||||||
import { VillageControllerFactory } from './VillageControllerFactory';
|
import { VillageFactory } from './VillageFactory';
|
||||||
|
|
||||||
export interface ExecutionSettings {
|
export interface ExecutionSettings {
|
||||||
pauseTs: number;
|
pauseTs: number;
|
||||||
@ -20,8 +20,7 @@ export interface ExecutionSettings {
|
|||||||
export class Executor {
|
export class Executor {
|
||||||
private readonly version: string;
|
private readonly version: string;
|
||||||
private readonly scheduler: Scheduler;
|
private readonly scheduler: Scheduler;
|
||||||
private readonly villageStateRepository: VillageStateRepository;
|
private villageFactory: VillageFactory;
|
||||||
private villageControllerFactory: VillageControllerFactory;
|
|
||||||
private grabberManager: GrabberManager;
|
private grabberManager: GrabberManager;
|
||||||
private statistics: Statistics;
|
private statistics: Statistics;
|
||||||
private executionState: ExecutionStorage;
|
private executionState: ExecutionStorage;
|
||||||
@ -30,16 +29,14 @@ export class Executor {
|
|||||||
constructor(
|
constructor(
|
||||||
version: string,
|
version: string,
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
villageStateRepository: VillageStateRepository,
|
villageFactory: VillageFactory,
|
||||||
villageControllerFactory: VillageControllerFactory,
|
|
||||||
grabberManager: GrabberManager,
|
grabberManager: GrabberManager,
|
||||||
statistics: Statistics,
|
statistics: Statistics,
|
||||||
logger: Logger
|
logger: Logger
|
||||||
) {
|
) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.villageStateRepository = villageStateRepository;
|
this.villageFactory = villageFactory;
|
||||||
this.villageControllerFactory = villageControllerFactory;
|
|
||||||
this.grabberManager = grabberManager;
|
this.grabberManager = grabberManager;
|
||||||
this.statistics = statistics;
|
this.statistics = statistics;
|
||||||
this.executionState = new ExecutionStorage();
|
this.executionState = new ExecutionStorage();
|
||||||
@ -109,7 +106,7 @@ export class Executor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async processActionCommand(action: Action, task: Task) {
|
private async processActionCommand(action: Action, task: Task) {
|
||||||
const actionHandler = createActionHandler(action.name, this.scheduler, this.villageStateRepository);
|
const actionHandler = createActionHandler(action.name, this.scheduler, this.villageFactory);
|
||||||
this.logger.info('Process action', action.name, actionHandler);
|
this.logger.info('Process action', action.name, actionHandler);
|
||||||
if (actionHandler) {
|
if (actionHandler) {
|
||||||
this.statistics.incrementAction(timestamp());
|
this.statistics.incrementAction(timestamp());
|
||||||
|
@ -20,7 +20,7 @@ export class BuildingContractGrabber extends Grabber {
|
|||||||
|
|
||||||
const contract = grabContractResources();
|
const contract = grabContractResources();
|
||||||
|
|
||||||
this.controller.updateResources(contract, {
|
this.taskCollection.updateResources(contract, {
|
||||||
type: ContractType.UpgradeBuilding,
|
type: ContractType.UpgradeBuilding,
|
||||||
buildId: building.buildId,
|
buildId: building.buildId,
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@ export class ForgePageGrabber extends Grabber {
|
|||||||
const contracts = grabImprovementContracts();
|
const contracts = grabImprovementContracts();
|
||||||
|
|
||||||
for (let { resources, unitId } of contracts) {
|
for (let { resources, unitId } of contracts) {
|
||||||
this.controller.updateResources(resources, {
|
this.taskCollection.updateResources(resources, {
|
||||||
type: ContractType.ImproveTrooper,
|
type: ContractType.ImproveTrooper,
|
||||||
buildId,
|
buildId,
|
||||||
unitId,
|
unitId,
|
||||||
@ -29,8 +29,7 @@ export class ForgePageGrabber extends Grabber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private grabTimer(): void {
|
private grabTimer(): void {
|
||||||
const storage = this.controller.getStorage();
|
|
||||||
const seconds = grabRemainingSeconds();
|
const seconds = grabRemainingSeconds();
|
||||||
storage.storeQueueTaskEnding(ProductionQueue.UpgradeUnit, seconds ? seconds + timestamp() : 0);
|
this.storage.storeQueueTaskEnding(ProductionQueue.UpgradeUnit, seconds ? seconds + timestamp() : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { VillageController } from '../VillageController';
|
import { VillageTaskCollection } from '../VillageTaskCollection';
|
||||||
|
import { VillageStorage } from '../Storage/VillageStorage';
|
||||||
|
|
||||||
export abstract class Grabber {
|
export abstract class Grabber {
|
||||||
protected controller: VillageController;
|
protected taskCollection: VillageTaskCollection;
|
||||||
|
protected storage: VillageStorage;
|
||||||
|
|
||||||
constructor(controller: VillageController) {
|
constructor(taskCollection: VillageTaskCollection, storage: VillageStorage) {
|
||||||
this.controller = controller;
|
this.taskCollection = taskCollection;
|
||||||
|
this.storage = storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract grab(): void;
|
abstract grab(): void;
|
||||||
|
@ -6,12 +6,12 @@ import { MarketPageGrabber } from './MarketPageGrabber';
|
|||||||
import { BuildingContractGrabber } from './BuildingContractGrabber';
|
import { BuildingContractGrabber } from './BuildingContractGrabber';
|
||||||
import { ForgePageGrabber } from './ForgePageGrabber';
|
import { ForgePageGrabber } from './ForgePageGrabber';
|
||||||
import { GuildHallPageGrabber } from './GuildHallPageGrabber';
|
import { GuildHallPageGrabber } from './GuildHallPageGrabber';
|
||||||
import { VillageControllerFactory } from '../VillageControllerFactory';
|
import { VillageFactory } from '../VillageFactory';
|
||||||
|
|
||||||
export class GrabberManager {
|
export class GrabberManager {
|
||||||
private factory: VillageControllerFactory;
|
private factory: VillageFactory;
|
||||||
|
|
||||||
constructor(factory: VillageControllerFactory) {
|
constructor(factory: VillageFactory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +23,16 @@ export class GrabberManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createGrabbers(): Array<Grabber> {
|
private createGrabbers(): Array<Grabber> {
|
||||||
const controller = this.factory.getActive();
|
const storage = this.factory.createStorageForActiveVillage();
|
||||||
|
const taskCollection = this.factory.createTaskCollectionForActiveVillage();
|
||||||
const grabbers: Array<Grabber> = [];
|
const grabbers: Array<Grabber> = [];
|
||||||
grabbers.push(new VillageResourceGrabber(controller));
|
grabbers.push(new VillageResourceGrabber(taskCollection, storage));
|
||||||
grabbers.push(new VillageOverviewPageGrabber(controller));
|
grabbers.push(new VillageOverviewPageGrabber(taskCollection, storage));
|
||||||
grabbers.push(new HeroPageGrabber(controller));
|
grabbers.push(new HeroPageGrabber(taskCollection, storage));
|
||||||
grabbers.push(new MarketPageGrabber(controller));
|
grabbers.push(new MarketPageGrabber(taskCollection, storage));
|
||||||
grabbers.push(new BuildingContractGrabber(controller));
|
grabbers.push(new BuildingContractGrabber(taskCollection, storage));
|
||||||
grabbers.push(new ForgePageGrabber(controller));
|
grabbers.push(new ForgePageGrabber(taskCollection, storage));
|
||||||
grabbers.push(new GuildHallPageGrabber(controller));
|
grabbers.push(new GuildHallPageGrabber(taskCollection, storage));
|
||||||
return grabbers;
|
return grabbers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ export class GuildHallPageGrabber extends Grabber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const seconds = grabRemainingSeconds();
|
const seconds = grabRemainingSeconds();
|
||||||
const storage = this.controller.getStorage();
|
this.storage.storeQueueTaskEnding(ProductionQueue.Celebration, seconds ? seconds + timestamp() : 0);
|
||||||
storage.storeQueueTaskEnding(ProductionQueue.Celebration, seconds ? seconds + timestamp() : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ export class MarketPageGrabber extends Grabber {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storage = this.controller.getStorage();
|
this.storage.storeIncomingMerchants(grabIncomingMerchants());
|
||||||
storage.storeIncomingMerchants(grabIncomingMerchants());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,12 @@ export class VillageOverviewPageGrabber extends Grabber {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storage = this.controller.getStorage();
|
this.storage.storeResourcesPerformance(grabResourcesPerformance());
|
||||||
storage.storeResourcesPerformance(grabResourcesPerformance());
|
this.storage.storeBuildingQueueInfo(this.grabBuildingQueueInfoOrDefault());
|
||||||
storage.storeBuildingQueueInfo(this.grabBuildingQueueInfoOrDefault());
|
|
||||||
|
|
||||||
const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault();
|
const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault();
|
||||||
const buildingEndTime = buildingQueueInfo.seconds ? buildingQueueInfo.seconds + timestamp() : 0;
|
const buildingEndTime = buildingQueueInfo.seconds ? buildingQueueInfo.seconds + timestamp() : 0;
|
||||||
storage.storeQueueTaskEnding(ProductionQueue.Building, buildingEndTime);
|
this.storage.storeQueueTaskEnding(ProductionQueue.Building, buildingEndTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private grabBuildingQueueInfoOrDefault() {
|
private grabBuildingQueueInfoOrDefault() {
|
||||||
|
@ -3,8 +3,7 @@ import { grabVillageResources, grabVillageResourceStorage } from '../Page/Resour
|
|||||||
|
|
||||||
export class VillageResourceGrabber extends Grabber {
|
export class VillageResourceGrabber extends Grabber {
|
||||||
grab(): void {
|
grab(): void {
|
||||||
const storage = this.controller.getStorage();
|
this.storage.storeResources(grabVillageResources());
|
||||||
storage.storeResources(grabVillageResources());
|
this.storage.storeResourceStorage(grabVillageResourceStorage());
|
||||||
storage.storeResourceStorage(grabVillageResourceStorage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,9 @@ import { ImmutableTaskList, Task, TaskId, uniqTaskId, withTime } from './Queue/T
|
|||||||
import { MARKET_ID } from './Core/Buildings';
|
import { MARKET_ID } from './Core/Buildings';
|
||||||
import { VillageRepositoryInterface } from './VillageRepository';
|
import { VillageRepositoryInterface } from './VillageRepository';
|
||||||
import { isProductionTask } from './Core/ProductionQueue';
|
import { isProductionTask } from './Core/ProductionQueue';
|
||||||
import { VillageControllerFactory } from './VillageControllerFactory';
|
import { VillageFactory } from './VillageFactory';
|
||||||
import { RunVillageProductionTask } from './Task/RunVillageProductionTask';
|
import { RunVillageProductionTask } from './Task/RunVillageProductionTask';
|
||||||
|
import { VillageNotFound } from './Errors';
|
||||||
|
|
||||||
export interface NextExecution {
|
export interface NextExecution {
|
||||||
task?: Task;
|
task?: Task;
|
||||||
@ -24,14 +25,14 @@ export class Scheduler {
|
|||||||
private taskQueue: TaskQueue;
|
private taskQueue: TaskQueue;
|
||||||
private actionQueue: ActionQueue;
|
private actionQueue: ActionQueue;
|
||||||
private villageRepository: VillageRepositoryInterface;
|
private villageRepository: VillageRepositoryInterface;
|
||||||
private villageControllerFactory: VillageControllerFactory;
|
private villageControllerFactory: VillageFactory;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
taskQueue: TaskQueue,
|
taskQueue: TaskQueue,
|
||||||
actionQueue: ActionQueue,
|
actionQueue: ActionQueue,
|
||||||
villageRepository: VillageRepositoryInterface,
|
villageRepository: VillageRepositoryInterface,
|
||||||
villageControllerFactory: VillageControllerFactory,
|
villageControllerFactory: VillageFactory,
|
||||||
logger: Logger
|
logger: Logger
|
||||||
) {
|
) {
|
||||||
this.taskQueue = taskQueue;
|
this.taskQueue = taskQueue;
|
||||||
@ -96,13 +97,13 @@ export class Scheduler {
|
|||||||
private replaceTask(task: Task): Task | undefined {
|
private replaceTask(task: Task): Task | undefined {
|
||||||
if (task.name === RunVillageProductionTask.name && task.args.villageId) {
|
if (task.name === RunVillageProductionTask.name && task.args.villageId) {
|
||||||
const villageId = task.args.villageId;
|
const villageId = task.args.villageId;
|
||||||
const controller = this.villageControllerFactory.create(villageId);
|
const controller = this.villageControllerFactory.createController(villageId);
|
||||||
const villageTask = controller.getReadyProductionTask();
|
const villageTask = controller.getReadyProductionTask();
|
||||||
if (villageTask) {
|
if (villageTask) {
|
||||||
this.removeTask(task.id);
|
this.removeTask(task.id);
|
||||||
const newTask = new Task(villageTask.id, 0, villageTask.name, {
|
const newTask = new Task(villageTask.id, 0, villageTask.name, {
|
||||||
...villageTask.args,
|
...villageTask.args,
|
||||||
villageId: controller.villageId,
|
villageId: controller.getVillageId(),
|
||||||
});
|
});
|
||||||
this.taskQueue.add(newTask);
|
this.taskQueue.add(newTask);
|
||||||
return newTask;
|
return newTask;
|
||||||
@ -113,7 +114,7 @@ export class Scheduler {
|
|||||||
|
|
||||||
scheduleTask(name: string, args: Args, ts?: number | undefined): void {
|
scheduleTask(name: string, args: Args, ts?: number | undefined): void {
|
||||||
if (isProductionTask(name) && args.villageId) {
|
if (isProductionTask(name) && args.villageId) {
|
||||||
const controller = this.villageControllerFactory.create(args.villageId);
|
const controller = this.villageControllerFactory.createController(args.villageId);
|
||||||
controller.addTask(name, args);
|
controller.addTask(name, args);
|
||||||
} else {
|
} else {
|
||||||
this.logger.info('Schedule task', name, args, ts);
|
this.logger.info('Schedule task', name, args, ts);
|
||||||
@ -143,7 +144,7 @@ export class Scheduler {
|
|||||||
const task = this.taskQueue.findById(taskId);
|
const task = this.taskQueue.findById(taskId);
|
||||||
const villageId = task ? task.args.villageId : undefined;
|
const villageId = task ? task.args.villageId : undefined;
|
||||||
if (villageId) {
|
if (villageId) {
|
||||||
const controller = this.villageControllerFactory.create(villageId);
|
const controller = this.villageControllerFactory.createController(villageId);
|
||||||
controller.removeTask(taskId);
|
controller.removeTask(taskId);
|
||||||
}
|
}
|
||||||
this.removeTask(taskId);
|
this.removeTask(taskId);
|
||||||
@ -156,7 +157,7 @@ export class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isProductionTask(task.name) && task.args.villageId) {
|
if (isProductionTask(task.name) && task.args.villageId) {
|
||||||
const controller = this.villageControllerFactory.create(task.args.villageId);
|
const controller = this.villageControllerFactory.createController(task.args.villageId);
|
||||||
controller.postponeTask(taskId, seconds);
|
controller.postponeTask(taskId, seconds);
|
||||||
this.removeTask(taskId);
|
this.removeTask(taskId);
|
||||||
} else {
|
} else {
|
||||||
@ -186,7 +187,7 @@ export class Scheduler {
|
|||||||
this.dropResourceTransferTasks(fromVillageId, toVillageId);
|
this.dropResourceTransferTasks(fromVillageId, toVillageId);
|
||||||
const village = this.villageRepository.all().find(v => v.id === toVillageId);
|
const village = this.villageRepository.all().find(v => v.id === toVillageId);
|
||||||
if (!village) {
|
if (!village) {
|
||||||
throw new Error('No village');
|
throw new VillageNotFound(`Village ${toVillageId} not found`);
|
||||||
}
|
}
|
||||||
this.scheduleTask(SendResourcesTask.name, {
|
this.scheduleTask(SendResourcesTask.name, {
|
||||||
villageId: fromVillageId,
|
villageId: fromVillageId,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { DataStorage } from '../DataStorage';
|
import { DataStorage } from '../DataStorage';
|
||||||
import { ActionStatistics, StatisticsStorageInterface } from '../Statistics';
|
|
||||||
import { LogStorageInterface, StorageLogRecord } from '../Logger';
|
import { LogStorageInterface, StorageLogRecord } from '../Logger';
|
||||||
|
|
||||||
const NAMESPACE = 'logs.v1';
|
const NAMESPACE = 'logs.v1';
|
||||||
|
@ -112,41 +112,7 @@ export class VillageStorage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addTask(task: Task): void {
|
storeTaskList(tasks: Array<Task>): void {
|
||||||
const tasks = this.getTasks();
|
|
||||||
tasks.push(task);
|
|
||||||
this.storeTaskList(tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
modifyTasks(predicate: (t: Task) => boolean, modifier: (t: Task) => Task): number {
|
|
||||||
const [matched, other] = this.split(predicate);
|
|
||||||
const modified = matched.map(modifier);
|
|
||||||
const modifiedCount = modified.length;
|
|
||||||
this.storeTaskList(modified.concat(other));
|
|
||||||
return modifiedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTasks(predicate: (t: Task) => boolean): number {
|
|
||||||
const [_, other] = this.split(predicate);
|
|
||||||
const result = other.length;
|
|
||||||
this.storeTaskList(other);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private split(predicate: (t: Task) => boolean): [TaskList, TaskList] {
|
|
||||||
const matched: TaskList = [];
|
|
||||||
const other: TaskList = [];
|
|
||||||
this.getTasks().forEach(t => {
|
|
||||||
if (predicate(t)) {
|
|
||||||
matched.push(t);
|
|
||||||
} else {
|
|
||||||
other.push(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return [matched, other];
|
|
||||||
}
|
|
||||||
|
|
||||||
private storeTaskList(tasks: Array<Task>): void {
|
|
||||||
this.storage.set(TASK_LIST_KEY, tasks);
|
this.storage.set(TASK_LIST_KEY, tasks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,36 @@
|
|||||||
import { Task, TaskId, uniqTaskId, withResources, withTime } from './Queue/TaskProvider';
|
import { VillageTaskCollection } from './VillageTaskCollection';
|
||||||
import { VillageStorage } from './Storage/VillageStorage';
|
import { Task, TaskId } from './Queue/TaskProvider';
|
||||||
import { Args } from './Queue/Args';
|
import { Args } from './Queue/Args';
|
||||||
import { isProductionTask, ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue';
|
import { VillageState } from './VillageState';
|
||||||
import { Resources } from './Core/Resources';
|
|
||||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
|
||||||
import { ForgeImprovementTask } from './Task/ForgeImprovementTask';
|
|
||||||
import { ContractType, ContractAttributes } from './Core/Contract';
|
|
||||||
import { timestamp } from './utils';
|
|
||||||
import { getProductionQueue } from './Task/TaskMap';
|
|
||||||
|
|
||||||
export class VillageController {
|
export class VillageController {
|
||||||
private readonly _villageId: number;
|
private readonly villageId: number;
|
||||||
private readonly _storage: VillageStorage;
|
private taskCollection: VillageTaskCollection;
|
||||||
|
private readonly state: VillageState;
|
||||||
|
|
||||||
constructor(villageId: number, storage: VillageStorage) {
|
constructor(villageId: number, taskCollection: VillageTaskCollection, state: VillageState) {
|
||||||
this._villageId = villageId;
|
this.villageId = villageId;
|
||||||
this._storage = storage;
|
this.taskCollection = taskCollection;
|
||||||
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
get villageId() {
|
getVillageId() {
|
||||||
return this._villageId;
|
return this.villageId;
|
||||||
}
|
|
||||||
|
|
||||||
getStorage(): VillageStorage {
|
|
||||||
return this._storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
addTask(name: string, args: Args) {
|
|
||||||
if (!isProductionTask(name)) {
|
|
||||||
throw new Error(`Task "${name}" is not production task`);
|
|
||||||
}
|
|
||||||
if (args.villageId !== this._villageId) {
|
|
||||||
throw new Error(`Task village id (${args.villageId}) not equal controller village id (${this._villageId}`);
|
|
||||||
}
|
|
||||||
const task = new Task(uniqTaskId(), 0, name, { villageId: this._villageId, ...args });
|
|
||||||
this._storage.addTask(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTasks(): Array<Task> {
|
|
||||||
return this._storage.getTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTask(taskId: TaskId) {
|
|
||||||
this._storage.removeTasks(t => t.id === taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTasksInProductionQueue(queue: ProductionQueue): Array<Task> {
|
|
||||||
return this._storage.getTasks().filter(task => getProductionQueue(task.name) === queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadyProductionTask(): Task | undefined {
|
getReadyProductionTask(): Task | undefined {
|
||||||
let sortedTasks: Array<Task> = [];
|
return this.taskCollection.getReadyProductionTask();
|
||||||
for (let queue of ProductionQueueTypes) {
|
}
|
||||||
const tasks = this.getTasksInProductionQueue(queue);
|
|
||||||
sortedTasks = sortedTasks.concat(tasks);
|
addTask(name: string, args: Args) {
|
||||||
}
|
this.taskCollection.addTask(name, args);
|
||||||
return sortedTasks.shift();
|
}
|
||||||
|
|
||||||
|
removeTask(taskId: TaskId) {
|
||||||
|
this.taskCollection.removeTask(taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
postponeTask(taskId: TaskId, seconds: number) {
|
postponeTask(taskId: TaskId, seconds: number) {
|
||||||
const modifyTime = withTime(timestamp() + seconds);
|
this.taskCollection.postponeTask(taskId, seconds);
|
||||||
this._storage.modifyTasks(task => task.id === taskId, modifyTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateResources(resources: Resources, attr: ContractAttributes): void {
|
|
||||||
if (attr.type === ContractType.UpgradeBuilding && attr.buildId) {
|
|
||||||
const predicate = (t: Task) => t.name === UpgradeBuildingTask.name && t.args.buildId === attr.buildId;
|
|
||||||
this._storage.modifyTasks(predicate, withResources(resources));
|
|
||||||
}
|
|
||||||
if (attr.type === ContractType.ImproveTrooper && attr.buildId && attr.unitId) {
|
|
||||||
const predicate = (t: Task) =>
|
|
||||||
t.name === ForgeImprovementTask.name &&
|
|
||||||
t.args.buildId === attr.buildId &&
|
|
||||||
t.args.unitId === attr.unitId;
|
|
||||||
this._storage.modifyTasks(predicate, withResources(resources));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getVillageRequiredResources(): Resources {
|
|
||||||
const tasks = this._storage.getTasks().filter(t => t.args.resources);
|
|
||||||
const first = tasks.shift();
|
|
||||||
if (first && first.args.resources) {
|
|
||||||
return Resources.fromObject(first.args.resources);
|
|
||||||
}
|
|
||||||
return Resources.zero();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTotalVillageRequiredResources(): Resources {
|
|
||||||
const tasks = this._storage.getTasks().filter(t => t.args.resources);
|
|
||||||
return tasks.reduce((acc, t) => acc.add(t.args.resources!), Resources.zero());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import { VillageController } from './VillageController';
|
|
||||||
import { VillageStorage } from './Storage/VillageStorage';
|
|
||||||
import { VillageRepository } from './VillageRepository';
|
|
||||||
|
|
||||||
export class VillageControllerFactory {
|
|
||||||
private villageRepository: VillageRepository;
|
|
||||||
|
|
||||||
constructor(villageRepository: VillageRepository) {
|
|
||||||
this.villageRepository = villageRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
create(villageId: number): VillageController {
|
|
||||||
const village = this.villageRepository.get(villageId);
|
|
||||||
return new VillageController(village.id, new VillageStorage(village.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
getActive(): VillageController {
|
|
||||||
const village = this.villageRepository.getActive();
|
|
||||||
return this.create(village.id);
|
|
||||||
}
|
|
||||||
}
|
|
57
src/VillageFactory.ts
Normal file
57
src/VillageFactory.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { VillageController } from './VillageController';
|
||||||
|
import { VillageStorage } from './Storage/VillageStorage';
|
||||||
|
import { VillageRepository } from './VillageRepository';
|
||||||
|
import { VillageTaskCollection } from './VillageTaskCollection';
|
||||||
|
import { VillageState, VillageStateFactory } from './VillageState';
|
||||||
|
|
||||||
|
export class VillageFactory {
|
||||||
|
private readonly villageRepository: VillageRepository;
|
||||||
|
|
||||||
|
constructor(villageRepository: VillageRepository) {
|
||||||
|
this.villageRepository = villageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
createStorage(villageId: number): VillageStorage {
|
||||||
|
const village = this.villageRepository.get(villageId);
|
||||||
|
return new VillageStorage(village.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
createStorageForActiveVillage(): VillageStorage {
|
||||||
|
const village = this.villageRepository.getActive();
|
||||||
|
return this.createStorage(village.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTaskCollection(villageId: number): VillageTaskCollection {
|
||||||
|
const village = this.villageRepository.get(villageId);
|
||||||
|
return new VillageTaskCollection(village.id, this.createStorage(villageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
createTaskCollectionForActiveVillage(): VillageTaskCollection {
|
||||||
|
const village = this.villageRepository.getActive();
|
||||||
|
return this.createTaskCollection(village.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
createState(villageId: number): VillageState {
|
||||||
|
const village = this.villageRepository.get(villageId);
|
||||||
|
const stateFactory = new VillageStateFactory(
|
||||||
|
this.villageRepository,
|
||||||
|
(id: number) => this.createStorage(id),
|
||||||
|
(id: number) => this.createTaskCollection(id)
|
||||||
|
);
|
||||||
|
return stateFactory.getVillageState(village.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllVillageStates(): Array<VillageState> {
|
||||||
|
const stateFactory = new VillageStateFactory(
|
||||||
|
this.villageRepository,
|
||||||
|
(id: number) => this.createStorage(id),
|
||||||
|
(id: number) => this.createTaskCollection(id)
|
||||||
|
);
|
||||||
|
return stateFactory.getAllVillageStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
createController(villageId: number): VillageController {
|
||||||
|
const village = this.villageRepository.get(villageId);
|
||||||
|
return new VillageController(village.id, this.createTaskCollection(village.id), this.createState(village.id));
|
||||||
|
}
|
||||||
|
}
|
@ -7,8 +7,7 @@ import { VillageNotFound } from './Errors';
|
|||||||
import { ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue';
|
import { ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue';
|
||||||
import { Task } from './Queue/TaskProvider';
|
import { Task } from './Queue/TaskProvider';
|
||||||
import { timestamp } from './utils';
|
import { timestamp } from './utils';
|
||||||
import { VillageControllerFactory } from './VillageControllerFactory';
|
import { VillageTaskCollection } from './VillageTaskCollection';
|
||||||
import { VillageController } from './VillageController';
|
|
||||||
|
|
||||||
interface VillageStorageState {
|
interface VillageStorageState {
|
||||||
resources: Resources;
|
resources: Resources;
|
||||||
@ -134,12 +133,12 @@ function taskResourceReducer(resources: Resources, task: Task) {
|
|||||||
|
|
||||||
function createProductionQueueState(
|
function createProductionQueueState(
|
||||||
queue: ProductionQueue,
|
queue: ProductionQueue,
|
||||||
controller: VillageController
|
storage: VillageStorage,
|
||||||
|
taskCollection: VillageTaskCollection
|
||||||
): VillageProductionQueueState {
|
): VillageProductionQueueState {
|
||||||
const storage = controller.getStorage();
|
|
||||||
const resources = storage.getResources();
|
const resources = storage.getResources();
|
||||||
const performance = storage.getResourcesPerformance();
|
const performance = storage.getResourcesPerformance();
|
||||||
const tasks = controller.getTasksInProductionQueue(queue);
|
const tasks = taskCollection.getTasksInProductionQueue(queue);
|
||||||
|
|
||||||
const firstTaskResources = tasks.slice(0, 1).reduce(taskResourceReducer, Resources.zero());
|
const firstTaskResources = tasks.slice(0, 1).reduce(taskResourceReducer, Resources.zero());
|
||||||
const allTaskResources = tasks.reduce(taskResourceReducer, Resources.zero());
|
const allTaskResources = tasks.reduce(taskResourceReducer, Resources.zero());
|
||||||
@ -156,33 +155,36 @@ function createProductionQueueState(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAllProductionQueueStates(controller: VillageController) {
|
function createAllProductionQueueStates(storage: VillageStorage, taskCollection: VillageTaskCollection) {
|
||||||
let result: { [queue: string]: VillageProductionQueueState } = {};
|
let result: { [queue: string]: VillageProductionQueueState } = {};
|
||||||
for (let queue of ProductionQueueTypes) {
|
for (let queue of ProductionQueueTypes) {
|
||||||
result[queue] = createProductionQueueState(queue, controller);
|
result[queue] = createProductionQueueState(queue, storage, taskCollection);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcFrontierResources(controller: VillageController): Resources {
|
function calcFrontierResources(taskCollection: VillageTaskCollection): Resources {
|
||||||
let result = Resources.zero();
|
let result = Resources.zero();
|
||||||
for (let queue of ProductionQueueTypes) {
|
for (let queue of ProductionQueueTypes) {
|
||||||
const tasks = controller.getTasksInProductionQueue(queue);
|
const tasks = taskCollection.getTasksInProductionQueue(queue);
|
||||||
const firstTaskResources = tasks.slice(0, 1).reduce(taskResourceReducer, Resources.zero());
|
const firstTaskResources = tasks.slice(0, 1).reduce(taskResourceReducer, Resources.zero());
|
||||||
result = result.add(firstTaskResources);
|
result = result.add(firstTaskResources);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVillageOwnState(village: Village, controller: VillageController): VillageOwnState {
|
function createVillageOwnState(
|
||||||
const storage = controller.getStorage();
|
village: Village,
|
||||||
|
storage: VillageStorage,
|
||||||
|
taskCollection: VillageTaskCollection
|
||||||
|
): VillageOwnState {
|
||||||
const resources = storage.getResources();
|
const resources = storage.getResources();
|
||||||
const resourceStorage = storage.getResourceStorage();
|
const resourceStorage = storage.getResourceStorage();
|
||||||
const performance = storage.getResourcesPerformance();
|
const performance = storage.getResourcesPerformance();
|
||||||
const buildQueueInfo = storage.getBuildingQueueInfo();
|
const buildQueueInfo = storage.getBuildingQueueInfo();
|
||||||
const requiredResources = controller.getVillageRequiredResources();
|
const requiredResources = taskCollection.getVillageRequiredResources();
|
||||||
const frontierResources = calcFrontierResources(controller);
|
const frontierResources = calcFrontierResources(taskCollection);
|
||||||
const totalRequiredResources = controller.getTotalVillageRequiredResources();
|
const totalRequiredResources = taskCollection.getTotalVillageRequiredResources();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: village.id,
|
id: village.id,
|
||||||
@ -196,18 +198,22 @@ function createVillageOwnState(village: Village, controller: VillageController):
|
|||||||
buildRemainingSeconds: buildQueueInfo.seconds,
|
buildRemainingSeconds: buildQueueInfo.seconds,
|
||||||
incomingResources: calcIncomingResources(storage),
|
incomingResources: calcIncomingResources(storage),
|
||||||
settings: storage.getSettings(),
|
settings: storage.getSettings(),
|
||||||
queues: createAllProductionQueueStates(controller),
|
queues: createAllProductionQueueStates(storage, taskCollection),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVillageOwnStates(
|
function createVillageOwnStates(
|
||||||
villages: Array<Village>,
|
villages: Array<Village>,
|
||||||
villageControllerFactory: VillageControllerFactory
|
storageFactory: VillageStorageFactory,
|
||||||
|
taskCollectionFactory: VillageTaskCollectionFactory
|
||||||
): VillageOwnStateDictionary {
|
): VillageOwnStateDictionary {
|
||||||
const result: VillageOwnStateDictionary = {};
|
const result: VillageOwnStateDictionary = {};
|
||||||
for (let village of villages) {
|
for (let village of villages) {
|
||||||
const villageController = villageControllerFactory.create(village.id);
|
result[village.id] = createVillageOwnState(
|
||||||
result[village.id] = createVillageOwnState(village, villageController);
|
village,
|
||||||
|
storageFactory(village.id),
|
||||||
|
taskCollectionFactory(village.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -226,27 +232,42 @@ function createVillageState(state: VillageOwnState, ownStates: VillageOwnStateDi
|
|||||||
|
|
||||||
function getVillageStates(
|
function getVillageStates(
|
||||||
villages: Array<Village>,
|
villages: Array<Village>,
|
||||||
villageControllerFactory: VillageControllerFactory
|
storageFactory: VillageStorageFactory,
|
||||||
|
taskCollectionFactory: VillageTaskCollectionFactory
|
||||||
): Array<VillageState> {
|
): Array<VillageState> {
|
||||||
const ownStates = createVillageOwnStates(villages, villageControllerFactory);
|
const ownStates = createVillageOwnStates(villages, storageFactory, taskCollectionFactory);
|
||||||
return villages.map(village => createVillageState(ownStates[village.id], ownStates));
|
return villages.map(village => createVillageState(ownStates[village.id], ownStates));
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VillageStateRepository {
|
interface VillageStorageFactory {
|
||||||
private villageRepository: VillageRepositoryInterface;
|
(villageId: number): VillageStorage;
|
||||||
private villageControllerFactory: VillageControllerFactory;
|
}
|
||||||
|
|
||||||
constructor(villageRepository: VillageRepositoryInterface, villageControllerFactory: VillageControllerFactory) {
|
interface VillageTaskCollectionFactory {
|
||||||
|
(villageId: number): VillageTaskCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VillageStateFactory {
|
||||||
|
private readonly villageRepository: VillageRepositoryInterface;
|
||||||
|
private readonly storageFactory: VillageStorageFactory;
|
||||||
|
private readonly taskCollectionFactory: VillageTaskCollectionFactory;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
villageRepository: VillageRepositoryInterface,
|
||||||
|
storageFactory: VillageStorageFactory,
|
||||||
|
taskCollectionFactory: VillageTaskCollectionFactory
|
||||||
|
) {
|
||||||
this.villageRepository = villageRepository;
|
this.villageRepository = villageRepository;
|
||||||
this.villageControllerFactory = villageControllerFactory;
|
this.storageFactory = storageFactory;
|
||||||
|
this.taskCollectionFactory = taskCollectionFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllVillageStates(): Array<VillageState> {
|
getAllVillageStates(): Array<VillageState> {
|
||||||
return getVillageStates(this.villageRepository.all(), this.villageControllerFactory);
|
return getVillageStates(this.villageRepository.all(), this.storageFactory, this.taskCollectionFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
getVillageState(villageId: number): VillageState {
|
getVillageState(villageId: number): VillageState {
|
||||||
const states = getVillageStates(this.villageRepository.all(), this.villageControllerFactory);
|
const states = getVillageStates(this.villageRepository.all(), this.storageFactory, this.taskCollectionFactory);
|
||||||
const needle = states.find(s => s.id === villageId);
|
const needle = states.find(s => s.id === villageId);
|
||||||
if (!needle) {
|
if (!needle) {
|
||||||
throw new VillageNotFound(`Village ${villageId} not found`);
|
throw new VillageNotFound(`Village ${villageId} not found`);
|
||||||
|
116
src/VillageTaskCollection.ts
Normal file
116
src/VillageTaskCollection.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { VillageStorage } from './Storage/VillageStorage';
|
||||||
|
import { Task, TaskId, TaskList, uniqTaskId, withResources, withTime } from './Queue/TaskProvider';
|
||||||
|
import { Args } from './Queue/Args';
|
||||||
|
import { isProductionTask, ProductionQueue, ProductionQueueTypes } from './Core/ProductionQueue';
|
||||||
|
import { getProductionQueue } from './Task/TaskMap';
|
||||||
|
import { timestamp } from './utils';
|
||||||
|
import { Resources } from './Core/Resources';
|
||||||
|
import { ContractAttributes, ContractType } from './Core/Contract';
|
||||||
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
|
import { ForgeImprovementTask } from './Task/ForgeImprovementTask';
|
||||||
|
|
||||||
|
export class VillageTaskCollection {
|
||||||
|
private readonly storage: VillageStorage;
|
||||||
|
private readonly villageId: number;
|
||||||
|
|
||||||
|
constructor(villageId: number, storage: VillageStorage) {
|
||||||
|
this.villageId = villageId;
|
||||||
|
this.storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTasks(): Array<Task> {
|
||||||
|
return this.storage.getTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private modifyTasks(predicate: (t: Task) => boolean, modifier: (t: Task) => Task): number {
|
||||||
|
const [matched, other] = this.split(predicate);
|
||||||
|
const modified = matched.map(modifier);
|
||||||
|
const modifiedCount = modified.length;
|
||||||
|
this.storage.storeTaskList(modified.concat(other));
|
||||||
|
return modifiedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeTasks(predicate: (t: Task) => boolean): number {
|
||||||
|
const [_, other] = this.split(predicate);
|
||||||
|
const result = other.length;
|
||||||
|
this.storage.storeTaskList(other);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private split(predicate: (t: Task) => boolean): [TaskList, TaskList] {
|
||||||
|
const matched: TaskList = [];
|
||||||
|
const other: TaskList = [];
|
||||||
|
this.getTasks().forEach(t => {
|
||||||
|
if (predicate(t)) {
|
||||||
|
matched.push(t);
|
||||||
|
} else {
|
||||||
|
other.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [matched, other];
|
||||||
|
}
|
||||||
|
|
||||||
|
addTask(name: string, args: Args) {
|
||||||
|
if (!isProductionTask(name)) {
|
||||||
|
throw new Error(`Task "${name}" is not production task`);
|
||||||
|
}
|
||||||
|
if (args.villageId !== this.villageId) {
|
||||||
|
throw new Error(`Task village id (${args.villageId}) not equal controller village id (${this.villageId}`);
|
||||||
|
}
|
||||||
|
const task = new Task(uniqTaskId(), 0, name, { villageId: this.villageId, ...args });
|
||||||
|
|
||||||
|
const tasks = this.getTasks();
|
||||||
|
tasks.push(task);
|
||||||
|
this.storage.storeTaskList(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTask(taskId: TaskId) {
|
||||||
|
this.removeTasks(t => t.id === taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTasksInProductionQueue(queue: ProductionQueue): Array<Task> {
|
||||||
|
return this.storage.getTasks().filter(task => getProductionQueue(task.name) === queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
getReadyProductionTask(): Task | undefined {
|
||||||
|
let sortedTasks: Array<Task> = [];
|
||||||
|
for (let queue of ProductionQueueTypes) {
|
||||||
|
const tasks = this.getTasksInProductionQueue(queue);
|
||||||
|
sortedTasks = sortedTasks.concat(tasks);
|
||||||
|
}
|
||||||
|
return sortedTasks.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
postponeTask(taskId: TaskId, seconds: number) {
|
||||||
|
const modifyTime = withTime(timestamp() + seconds);
|
||||||
|
this.modifyTasks(task => task.id === taskId, modifyTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateResources(resources: Resources, attr: ContractAttributes): void {
|
||||||
|
if (attr.type === ContractType.UpgradeBuilding && attr.buildId) {
|
||||||
|
const predicate = (t: Task) => t.name === UpgradeBuildingTask.name && t.args.buildId === attr.buildId;
|
||||||
|
this.modifyTasks(predicate, withResources(resources));
|
||||||
|
}
|
||||||
|
if (attr.type === ContractType.ImproveTrooper && attr.buildId && attr.unitId) {
|
||||||
|
const predicate = (t: Task) =>
|
||||||
|
t.name === ForgeImprovementTask.name &&
|
||||||
|
t.args.buildId === attr.buildId &&
|
||||||
|
t.args.unitId === attr.unitId;
|
||||||
|
this.modifyTasks(predicate, withResources(resources));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getVillageRequiredResources(): Resources {
|
||||||
|
const tasks = this.storage.getTasks().filter(t => t.args.resources);
|
||||||
|
const first = tasks.shift();
|
||||||
|
if (first && first.args.resources) {
|
||||||
|
return Resources.fromObject(first.args.resources);
|
||||||
|
}
|
||||||
|
return Resources.zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalVillageRequiredResources(): Resources {
|
||||||
|
const tasks = this.storage.getTasks().filter(t => t.args.resources);
|
||||||
|
return tasks.reduce((acc, t) => acc.add(t.args.resources!), Resources.zero());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user